Merge branch 'release-3.15'
diff --git a/Help/command/add_test.rst b/Help/command/add_test.rst
index 46b9b63..884b2ee 100644
--- a/Help/command/add_test.rst
+++ b/Help/command/add_test.rst
@@ -7,7 +7,8 @@
 
   add_test(NAME <name> COMMAND <command> [<arg>...]
            [CONFIGURATIONS <config>...]
-           [WORKING_DIRECTORY <dir>])
+           [WORKING_DIRECTORY <dir>]
+           [COMMAND_EXPAND_LISTS])
 
 Adds a test called ``<name>``.  The test name may not contain spaces,
 quotes, or other characters special in CMake syntax.  The options are:
@@ -28,6 +29,11 @@
   directory set to the build directory corresponding to the
   current source directory.
 
+``COMMAND_EXPAND_LISTS``
+  Lists in ``COMMAND`` arguments will be expanded, including those
+  created with
+  :manual:`generator expressions <cmake-generator-expressions(7)>`.
+
 The given test command is expected to exit with code ``0`` to pass and
 non-zero to fail, or vice-versa if the :prop_test:`WILL_FAIL` test
 property is set.  Any output written to stdout or stderr will be
diff --git a/Help/command/file.rst b/Help/command/file.rst
index f99021e..666a532 100644
--- a/Help/command/file.rst
+++ b/Help/command/file.rst
@@ -13,6 +13,7 @@
     file(`STRINGS`_ <filename> <out-var> [...])
     file(`\<HASH\> <HASH_>`_ <filename> <out-var>)
     file(`TIMESTAMP`_ <filename> <out-var> [...])
+    file(`GET_RUNTIME_DEPENDENCIES`_ [...])
 
   `Writing`_
     file({`WRITE`_ | `APPEND`_} <filename> <content>...)
@@ -130,6 +131,273 @@
 See the :command:`string(TIMESTAMP)` command for documentation of
 the ``<format>`` and ``UTC`` options.
 
+.. _GET_RUNTIME_DEPENDENCIES:
+
+.. code-block:: cmake
+
+  file(GET_RUNTIME_DEPENDENCIES
+    [RESOLVED_DEPENDENCIES_VAR <deps_var>]
+    [UNRESOLVED_DEPENDENCIES_VAR <unresolved_deps_var>]
+    [CONFLICTING_DEPENDENICES_PREFIX <conflicting_deps_prefix>]
+    [EXECUTABLES [<executable_files>...]]
+    [LIBRARIES [<library_files>...]]
+    [MODULES [<module_files>...]]
+    [DIRECTORIES [<directories>...]]
+    [BUNDLE_EXECUTABLE <bundle_executable_file>]
+    [PRE_INCLUDE_REGEXES [<regexes>...]]
+    [PRE_EXCLUDE_REGEXES [<regexes>...]]
+    [POST_INCLUDE_REGEXES [<regexes>...]]
+    [POST_EXCLUDE_REGEXES [<regexes>...]]
+    )
+
+Recursively get the list of libraries depended on by the given files.
+
+Please note that this sub-command is not intended to be used in project mode.
+Instead, use it in an :command:`install(CODE)` or :command:`install(SCRIPT)`
+block. For example:
+
+.. code-block:: cmake
+
+  install(CODE [[
+    file(GET_RUNTIME_DEPENDENCIES
+      # ...
+      )
+    ]])
+
+The arguments are as follows:
+
+``RESOLVED_DEPENDENCIES_VAR <deps_var>``
+  Name of the variable in which to store the list of resolved dependencies.
+
+``UNRESOLVED_DEPENDENCIES_VAR <unresolved_deps_var>``
+  Name of the variable in which to store the list of unresolved dependencies.
+  If this variable is not specified, and there are any unresolved dependencies,
+  an error is issued.
+
+``CONFLICTING_DEPENDENCIES_PREFIX <conflicting_deps_prefix>``
+  Variable prefix in which to store conflicting dependency information.
+  Dependencies are conflicting if two files with the same name are found in
+  two different directories. The list of filenames that conflict are stored in
+  ``<conflicting_deps_prefix>_FILENAMES``. For each filename, the list of paths
+  that were found for that filename are stored in
+  ``<conflicting_deps_prefix>_<filename>``.
+
+``EXECUTABLES <executable_files>``
+  List of executable files to read for dependencies. These are executables that
+  are typically created with :command:`add_executable`, but they do not have to
+  be created by CMake. On Apple platforms, the paths to these files determine
+  the value of ``@executable_path`` when recursively resolving the libraries.
+  Specifying ``STATIC`` libraries, ``MODULE`` s, or ``SHARED`` libraries here
+  will result in undefined behavior.
+
+``LIBRARIES <library_files>``
+  List of library files to read for dependencies. These are libraries that are
+  typically created with :command:`add_library(SHARED)`, but they do not have
+  to be created by CMake. Specifying ``STATIC`` libraries, ``MODULE`` s, or
+  executables here will result in undefined behavior.
+
+``MODULES <module_files>``
+  List of loadable module files to read for dependencies. These are modules
+  that are typically created with :command:`add_library(MODULE)`, but they do
+  not have to be created by CMake. They are typically used by calling
+  ``dlopen()`` at runtime rather than linked at link time with ``ld -l``.
+  Specifying ``STATIC`` libraries, ``SHARED`` libraries, or executables here
+  will result in undefined behavior.
+
+``DIRECTORIES <directories>``
+  List of additional directories to search for dependencies. On Linux
+  platforms, these directories are searched if the dependency is not found in
+  any of the other usual paths. If it is found in such a directory, a warning
+  is issued, because it means that the file is incomplete (it does not list all
+  of the directories that contain its dependencies.) On Windows platforms,
+  these directories are searched if the dependency is not found in any of the
+  other search paths, but no warning is issued, because searching other paths
+  is a normal part of Windows dependency resolution. On Apple platforms, this
+  argument has no effect.
+
+``BUNDLE_EXECTUBLE <bundle_executable_file>``
+  Executable to treat as the "bundle executable" when resolving libraries. On
+  Apple platforms, this argument determines the value of ``@executable_path``
+  when recursively resolving libraries for ``LIBRARIES`` and ``MODULES`` files.
+  It has no effect on ``EXECUTABLES`` files. On other platforms, it has no
+  effect. This is typically (but not always) one of the executables in the
+  ``EXECUTABLES`` argument which designates the "main" executable of the
+  package.
+
+The following arguments specify filters for including or excluding libraries to
+be resolved. See below for a full description of how they work.
+
+``PRE_INCLUDE_REGEXES <regexes>``
+  List of pre-include regexes through which to filter the names of
+  not-yet-resolved dependencies.
+
+``PRE_EXCLUDE_REGEXES <regexes>``
+  List of pre-exclude regexes through which to filter the names of
+  not-yet-resolved dependencies.
+
+``POST_INCLUDE_REGEXES <regexes>``
+  List of post-include regexes through which to filter the names of resolved
+  dependencies.
+
+``POST_EXCLUDE_REGEXES <regexes>``
+  List of post-exclude regexes through which to filter the names of resolved
+  dependencies.
+
+These arguments can be used to blacklist unwanted system libraries when
+resolving the dependencies, or to whitelist libraries from a specific
+directory. The filtering works as follows:
+
+1. If the not-yet-resolved dependency matches any of the
+   ``PRE_INCLUDE_REGEXES``, steps 2 and 3 are skipped, and the dependency
+   resolution proceeds to step 4.
+2. If the not-yet-resolved dependency matches any of the
+   ``PRE_EXCLUDE_REGEXES``, dependency resolution stops for that dependency.
+3. Otherwise, dependency resolution proceeds.
+4. ``file(GET_RUNTIME_DEPENDENCIES)`` searches for the dependency according to
+   the linking rules of the platform (see below).
+5. If the dependency is found, and its full path matches one of the
+   ``POST_INCLUDE_REGEXES``, the full path is added to the resolved
+   dependencies, and ``file(GET_RUNTIME_DEPENDENCIES)`` recursively resolves
+   that library's own dependencies. Otherwise, resolution proceeds to step 6.
+6. If the dependency is found, but its full path matches one of the
+   ``POST_EXCLUDE_REGEXES``, it is not added to the resolved dependencies, and
+   dependency resolution stops for that dependency.
+7. If the dependency is found, and its full path does not match either
+   ``POST_INCLUDE_REGEXES`` or ``POST_EXCLUDE_REGEXES``, the full path is added
+   to the resolved dependencies, and ``file(GET_RUNTIME_DEPENDENCIES)``
+   recursively resolves that library's own dependencies.
+
+Different platforms have different rules for how dependencies are resolved.
+These specifics are described here.
+
+On Linux platforms, library resolution works as follows:
+
+1. If the depending file does not have any ``RUNPATH`` entries, and the library
+   exists in one of the depending file's ``RPATH`` entries, or its parents', in
+   that order, the dependency is resolved to that file.
+2. Otherwise, if the depending file has any ``RUNPATH`` entries, and the
+   library exists in one of those entries, the dependency is resolved to that
+   file.
+3. Otherwise, if the library exists in one of the directories listed by
+   ``ldconfig``, the dependency is resolved to that file.
+4. Otherwise, if the library exists in one of the ``DIRECTORIES`` entries, the
+   dependency is resolved to that file. In this case, a warning is issued,
+   because finding a file in one of the ``DIRECTORIES`` means that the
+   depending file is not complete (it does not list all the directories from
+   which it pulls dependencies.)
+5. Otherwise, the dependency is unresolved.
+
+On Windows platforms, library resolution works as follows:
+
+1. The dependent DLL name is converted to lowercase. Windows DLL names are
+   case-insensitive, and some linkers mangle the case of the DLL dependency
+   names. However, this makes it more difficult for ``PRE_INCLUDE_REGEXES``,
+   ``PRE_EXCLUDE_REGEXES``, ``POST_INCLUDE_REGEXES``, and
+   ``POST_EXCLUDE_REGEXES`` to properly filter DLL names - every regex would
+   have to check for both uppercase and lowercase letters. For example:
+
+   .. code-block:: cmake
+
+     file(GET_RUNTIME_DEPENDENCIES
+       # ...
+       PRE_INCLUDE_REGEXES "^[Mm][Yy][Ll][Ii][Bb][Rr][Aa][Rr][Yy]\\.[Dd][Ll][Ll]$"
+       )
+
+   Converting the DLL name to lowercase allows the regexes to only match
+   lowercase names, thus simplifying the regex. For example:
+
+   .. code-block:: cmake
+
+     file(GET_RUNTIME_DEPENDENCIES
+       # ...
+       PRE_INCLUDE_REGEXES "^mylibrary\\.dll$"
+       )
+
+   This regex will match ``mylibrary.dll`` regardless of how it is cased,
+   either on disk or in the depending file. (For example, it will match
+   ``mylibrary.dll``, ``MyLibrary.dll``, and ``MYLIBRARY.DLL``.)
+
+   Please note that the directory portion of any resolved DLLs retains its
+   casing and is not converted to lowercase. Only the filename portion is
+   converted.
+
+2. (**Not yet implemented**) If the depending file is a Windows Store app, and
+   the dependency is listed as a dependency in the application's package
+   manifest, the dependency is resolved to that file.
+3. Otherwise, if the library exists in the same directory as the depending
+   file, the dependency is resolved to that file.
+4. Otherwise, if the library exists in either the operating system's
+   ``system32`` directory or the ``Windows`` directory, in that order, the
+   dependency is resolved to that file.
+5. Otherwise, if the library exists in one of the directories specified by
+   ``DIRECTORIES``, in the order they are listed, the dependency is resolved to
+   that file. (In this case, a warning is not issued, because searching other
+   directories is a normal part of Windows library resolution.)
+6. Otherwise, the dependency is unresolved.
+
+On Apple platforms, library resolution works as follows:
+
+1. If the dependency starts with ``@executable_path/``, and an ``EXECUTABLES``
+   argument is in the process of being resolved, and replacing
+   ``@executable_path/`` with the directory of the executable yields an
+   existing file, the dependency is resolved to that file.
+2. Otherwise, if the dependency starts with ``@executable_path/``, and there is
+   a ``BUNDLE_EXECUTABLE`` argument, and replacing ``@executable_path/`` with
+   the directory of the bundle executable yields an existing file, the
+   dependency is resolved to that file.
+3. Otherwise, if the dependency starts with ``@loader_path/``, and replacing
+   ``@loader_path/`` with the directory of the depending file yields an
+   existing file, the dependency is resolved to that file.
+4. Otherwise, if the dependency starts with ``@rpath/``, and replacing
+   ``@rpath/`` with one of the ``RPATH`` entries of the depending file yields
+   an existing file, the dependency is resolved to that file. (Note that
+   ``RPATH`` entries that start with ``@executable_path/`` or ``@loader_path/``
+   also have these items replaced with the appropriate path.)
+5. Otherwise, if the dependency is an absolute file that exists, the dependency
+   is resolved to that file.
+6. Otherwise, the dependency is unresolved.
+
+This function accepts several variables that determine which tool is used for
+dependency resolution:
+
+.. variable:: CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM
+
+  Determines which operating system and executable format the files are built
+  for. This could be one of several values:
+
+  * ``linux+elf``
+  * ``windows+pe``
+  * ``macos+macho``
+
+  If this variable is not specified, it is determined automatically by system
+  introspection.
+
+.. variable:: CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL
+
+  Determines the tool to use for dependency resolution. It could be one of
+  several values, depending on the value of
+  :variable:`CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM`:
+
+  ================================================= =============================================
+     ``CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM``       ``CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL``
+  ================================================= =============================================
+  ``linux+elf``                                     ``objdump``
+  ``windows+pe``                                    ``dumpbin``
+  ``windows+pe``                                    ``objdump``
+  ``macos+macho``                                   ``otool``
+  ================================================= =============================================
+
+  If this variable is not specified, it is determined automatically by system
+  introspection.
+
+.. variable:: CMAKE_GET_RUNTIME_DEPENDENCIES_COMMAND
+
+  Determines the path to the tool to use for dependency resolution. This is the
+  actual path to ``objdump``, ``dumpbin``, or ``otool``.
+
+  If this variable is not specified, it is determined automatically by system
+  introspection.
+
 Writing
 ^^^^^^^
 
diff --git a/Help/manual/cmake-policies.7.rst b/Help/manual/cmake-policies.7.rst
index 1d023cb..bac1c6c 100644
--- a/Help/manual/cmake-policies.7.rst
+++ b/Help/manual/cmake-policies.7.rst
@@ -57,6 +57,7 @@
 .. toctree::
    :maxdepth: 1
 
+   CMP0095: RPATH entries are properly escaped in the intermediary CMake install script. </policy/CMP0095>
    CMP0094: FindPython3, FindPython2 and FindPython use LOCATION for lookup strategy. </policy/CMP0094>
    CMP0093: FindBoost reports Boost_VERSION in x.y.z format. </policy/CMP0093>
    CMP0092: MSVC warning flags are not in CMAKE_{C,CXX}_FLAGS by default. </policy/CMP0092>
diff --git a/Help/policy/CMP0095.rst b/Help/policy/CMP0095.rst
new file mode 100644
index 0000000..4c56a05
--- /dev/null
+++ b/Help/policy/CMP0095.rst
@@ -0,0 +1,30 @@
+CMP0095
+-------
+
+``RPATH`` entries are properly escaped in the intermediary CMake install script.
+
+In CMake 3.15 and earlier, ``RPATH`` entries set via
+:variable:`CMAKE_INSTALL_RPATH` or via :prop_tgt:`INSTALL_RPATH` have not been
+escaped before being inserted into the ``cmake_install.cmake`` script. Dynamic
+linkers on ELF-based systems (e.g. Linux and FreeBSD) allow certain keywords in
+``RPATH`` entries, such as ``${ORIGIN}`` (More details are available in the
+``ld.so`` man pages on those systems). The syntax of these keywords can match
+CMake's variable syntax. In order to not be substituted (usually to an empty
+string) already by the intermediary ``cmake_install.cmake`` script, the user had
+to double-escape such ``RPATH`` keywords, e.g.
+``set(CMAKE_INSTALL_RPATH "\\\${ORIGIN}/../lib")``. Since the intermediary
+``cmake_install.cmake`` script is an implementation detail of CMake, CMake 3.16
+and later will make sure ``RPATH`` entries are inserted literally by escaping
+any coincidental CMake syntax.
+
+The ``OLD`` behavior of this policy is to not escape ``RPATH`` entries in the
+intermediary ``cmake_install.cmake`` script. The ``NEW`` behavior is to properly
+escape coincidental CMake syntax in ``RPATH`` entries when generating the
+intermediary ``cmake_install.cmake`` script.
+
+This policy was introduced in CMake version 3.16. CMake version |release| warns
+when the policy is not set and detected usage of CMake-like syntax 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/0-sample-topic.rst b/Help/release/dev/0-sample-topic.rst
new file mode 100644
index 0000000..e4cc01e
--- /dev/null
+++ b/Help/release/dev/0-sample-topic.rst
@@ -0,0 +1,7 @@
+0-sample-topic
+--------------
+
+* This is a sample release note for the change in a topic.
+  Developers should add similar notes for each topic branch
+  making a noteworthy change.  Each document should be named
+  and titled to match the topic name to avoid merge conflicts.
diff --git a/Help/release/dev/CMP0095.rst b/Help/release/dev/CMP0095.rst
new file mode 100644
index 0000000..21d0550
--- /dev/null
+++ b/Help/release/dev/CMP0095.rst
@@ -0,0 +1,5 @@
+CMP0095
+-------
+
+* ``RPATH`` entries are properly escaped in the intermediary CMake install script.
+  See policy :policy:`CMP0095`.
diff --git a/Help/release/dev/add_test-expand_lists.rst b/Help/release/dev/add_test-expand_lists.rst
new file mode 100644
index 0000000..88d26b7
--- /dev/null
+++ b/Help/release/dev/add_test-expand_lists.rst
@@ -0,0 +1,6 @@
+add_test-expand_lists
+---------------------
+
+* The command :command:`add_test` learned the option ``COMMAND_EXPAND_LISTS``
+  which causes lists in the ``COMMAND`` argument to be expanded, including
+  lists created by generator expressions.
diff --git a/Help/release/dev/get-runtime-dependencies.rst b/Help/release/dev/get-runtime-dependencies.rst
new file mode 100644
index 0000000..b9dc6e6
--- /dev/null
+++ b/Help/release/dev/get-runtime-dependencies.rst
@@ -0,0 +1,9 @@
+get-runtime-dependencies
+------------------------
+
+* The :command:`file` command learned a new sub-command,
+  ``GET_RUNTIME_DEPENDENCIES``, which allows you to recursively get the list of
+  libraries linked by an executable or library. This sub-command is intended as
+  a replacement for :module:`GetPrerequisites`.
+* The :module:`GetPrerequisites` module has been deprecated, as it has been
+  superceded by :command:`file(GET_RUNTIME_DEPENDENCIES)`.
diff --git a/Help/release/index.rst b/Help/release/index.rst
index 35a47aa..62032e6 100644
--- a/Help/release/index.rst
+++ b/Help/release/index.rst
@@ -7,6 +7,8 @@
   This file should include the adjacent "dev.txt" file
   in development versions but not in release versions.
 
+.. include:: dev.txt
+
 Releases
 ========
 
diff --git a/Modules/CMakeSwiftInformation.cmake b/Modules/CMakeSwiftInformation.cmake
index 21f18d4..6f99c1f 100644
--- a/Modules/CMakeSwiftInformation.cmake
+++ b/Modules/CMakeSwiftInformation.cmake
@@ -23,6 +23,8 @@
 set(CMAKE_Swift_COMPILER_ARG1 -frontend)
 set(CMAKE_Swift_DEFINE_FLAG -D)
 set(CMAKE_Swift_FRAMEWORK_SEARCH_FLAG "-F ")
+set(CMAKE_Swift_LIBRARY_PATH_FLAG "-L ")
+set(CMAKE_Swift_LIBRARY_PATH_TERMINATOR "")
 set(CMAKE_Swift_LINKER_WRAPPER_FLAG "-Xlinker" " ")
 set(CMAKE_Swift_RESPONSE_FILE_LINK_FLAG @)
 
diff --git a/Modules/GetPrerequisites.cmake b/Modules/GetPrerequisites.cmake
index 5be4676..41d9dc1 100644
--- a/Modules/GetPrerequisites.cmake
+++ b/Modules/GetPrerequisites.cmake
@@ -5,6 +5,10 @@
 GetPrerequisites
 ----------------
 
+.. deprecated:: 3.16
+
+  Use :command:`file(GET_RUNTIME_DEPENDENCIES)` instead.
+
 Functions to analyze and list executable file prerequisites.
 
 This module provides functions to list the .dll, .dylib or .so files
diff --git a/Modules/Internal/CPack/CPackDeb.cmake b/Modules/Internal/CPack/CPackDeb.cmake
index 89dc6f0..7791822 100644
--- a/Modules/Internal/CPack/CPackDeb.cmake
+++ b/Modules/Internal/CPack/CPackDeb.cmake
@@ -45,7 +45,7 @@
       ERROR_QUIET
       OUTPUT_STRIP_TRAILING_WHITESPACE)
     if(result EQUAL 0)
-      string(REGEX MATCH "\\(SONAME\\)[^\n]*\\[([^\n]+)\\.so\\.([^\n]*)\\]" soname "${output}")
+      string(REGEX MATCH "\\(?SONAME\\)?[^\n]*\\[([^\n]+)\\.so\\.([^\n]*)\\]" soname "${output}")
       set(${libname} "${CMAKE_MATCH_1}" PARENT_SCOPE)
       set(${version} "${CMAKE_MATCH_2}" PARENT_SCOPE)
     else()
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index 695e075..0316532 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -146,6 +146,28 @@
   cmArgumentParser.cxx
   cmArgumentParser.h
   cmBase32.cxx
+  cmBinUtilsLinker.cxx
+  cmBinUtilsLinker.h
+  cmBinUtilsLinuxELFGetRuntimeDependenciesTool.cxx
+  cmBinUtilsLinuxELFGetRuntimeDependenciesTool.h
+  cmBinUtilsLinuxELFLinker.cxx
+  cmBinUtilsLinuxELFLinker.h
+  cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.cxx
+  cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.h
+  cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.cxx
+  cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.h
+  cmBinUtilsMacOSMachOLinker.cxx
+  cmBinUtilsMacOSMachOLinker.h
+  cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.cxx
+  cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.h
+  cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.cxx
+  cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.h
+  cmBinUtilsWindowsPEGetRuntimeDependenciesTool.cxx
+  cmBinUtilsWindowsPEGetRuntimeDependenciesTool.h
+  cmBinUtilsWindowsPELinker.cxx
+  cmBinUtilsWindowsPELinker.h
+  cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.cxx
+  cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.h
   cmCacheManager.cxx
   cmCacheManager.h
   cmCLocaleEnvironmentScope.h
@@ -295,6 +317,10 @@
   cmInstallTargetGenerator.cxx
   cmInstallDirectoryGenerator.h
   cmInstallDirectoryGenerator.cxx
+  cmLDConfigLDConfigTool.cxx
+  cmLDConfigLDConfigTool.h
+  cmLDConfigTool.cxx
+  cmLDConfigTool.h
   cmLinkedTree.h
   cmLinkItem.cxx
   cmLinkItem.h
@@ -338,7 +364,6 @@
   cmProcessOutput.h
   cmProcessTools.cxx
   cmProcessTools.h
-  cmProperty.cxx
   cmProperty.h
   cmPropertyDefinition.cxx
   cmPropertyDefinition.h
@@ -360,6 +385,8 @@
   cmQtAutoRcc.h
   cmRST.cxx
   cmRST.h
+  cmRuntimeDependencyArchive.cxx
+  cmRuntimeDependencyArchive.h
   cmScriptGenerator.h
   cmScriptGenerator.cxx
   cmSourceFile.cxx
diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake
index e22d37c..9bef298 100644
--- a/Source/CMakeVersion.cmake
+++ b/Source/CMakeVersion.cmake
@@ -1,5 +1,5 @@
 # CMake version number components.
 set(CMake_VERSION_MAJOR 3)
 set(CMake_VERSION_MINOR 15)
-set(CMake_VERSION_PATCH 0)
-set(CMake_VERSION_RC 1)
+set(CMake_VERSION_PATCH 20190618)
+#set(CMake_VERSION_RC 1)
diff --git a/Source/cmAddCustomCommandCommand.cxx b/Source/cmAddCustomCommandCommand.cxx
index 0be3c85..6eb38bd 100644
--- a/Source/cmAddCustomCommandCommand.cxx
+++ b/Source/cmAddCustomCommandCommand.cxx
@@ -95,30 +95,29 @@
   MAKE_STATIC_KEYWORD(VERBATIM);
   MAKE_STATIC_KEYWORD(WORKING_DIRECTORY);
 #undef MAKE_STATIC_KEYWORD
-  static std::unordered_set<std::string> keywords;
-  if (keywords.empty()) {
-    keywords.insert(keyAPPEND);
-    keywords.insert(keyARGS);
-    keywords.insert(keyBYPRODUCTS);
-    keywords.insert(keyCOMMAND);
-    keywords.insert(keyCOMMAND_EXPAND_LISTS);
-    keywords.insert(keyCOMMENT);
-    keywords.insert(keyDEPENDS);
-    keywords.insert(keyDEPFILE);
-    keywords.insert(keyIMPLICIT_DEPENDS);
-    keywords.insert(keyJOB_POOL);
-    keywords.insert(keyMAIN_DEPENDENCY);
-    keywords.insert(keyOUTPUT);
-    keywords.insert(keyOUTPUTS);
-    keywords.insert(keyPOST_BUILD);
-    keywords.insert(keyPRE_BUILD);
-    keywords.insert(keyPRE_LINK);
-    keywords.insert(keySOURCE);
-    keywords.insert(keyTARGET);
-    keywords.insert(keyUSES_TERMINAL);
-    keywords.insert(keyVERBATIM);
-    keywords.insert(keyWORKING_DIRECTORY);
-  }
+  static std::unordered_set<std::string> const keywords{
+    keyAPPEND,
+    keyARGS,
+    keyBYPRODUCTS,
+    keyCOMMAND,
+    keyCOMMAND_EXPAND_LISTS,
+    keyCOMMENT,
+    keyDEPENDS,
+    keyDEPFILE,
+    keyIMPLICIT_DEPENDS,
+    keyJOB_POOL,
+    keyMAIN_DEPENDENCY,
+    keyOUTPUT,
+    keyOUTPUTS,
+    keyPOST_BUILD,
+    keyPRE_BUILD,
+    keyPRE_LINK,
+    keySOURCE,
+    keyTARGET,
+    keyUSES_TERMINAL,
+    keyVERBATIM,
+    keyWORKING_DIRECTORY
+  };
 
   for (std::string const& copy : args) {
     if (keywords.count(copy)) {
diff --git a/Source/cmAddTestCommand.cxx b/Source/cmAddTestCommand.cxx
index bf28702..b0c462b 100644
--- a/Source/cmAddTestCommand.cxx
+++ b/Source/cmAddTestCommand.cxx
@@ -58,6 +58,7 @@
   std::vector<std::string> configurations;
   std::string working_directory;
   std::vector<std::string> command;
+  bool command_expand_lists = false;
 
   // Read the arguments.
   enum Doing
@@ -88,6 +89,13 @@
         return false;
       }
       doing = DoingWorkingDirectory;
+    } else if (args[i] == "COMMAND_EXPAND_LISTS") {
+      if (command_expand_lists) {
+        this->SetError(" may be given at most one COMMAND_EXPAND_LISTS.");
+        return false;
+      }
+      command_expand_lists = true;
+      doing = DoingNone;
     } else if (doing == DoingName) {
       name = args[i];
       doing = DoingNone;
@@ -134,6 +142,7 @@
   if (!working_directory.empty()) {
     test->SetProperty("WORKING_DIRECTORY", working_directory.c_str());
   }
+  test->SetCommandExpandLists(command_expand_lists);
   this->Makefile->AddTestGenerator(new cmTestGenerator(test, configurations));
 
   return true;
diff --git a/Source/cmBinUtilsLinker.cxx b/Source/cmBinUtilsLinker.cxx
new file mode 100644
index 0000000..3dac85c
--- /dev/null
+++ b/Source/cmBinUtilsLinker.cxx
@@ -0,0 +1,15 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#include "cmBinUtilsLinker.h"
+#include "cmRuntimeDependencyArchive.h"
+
+cmBinUtilsLinker::cmBinUtilsLinker(cmRuntimeDependencyArchive* archive)
+  : Archive(archive)
+{
+}
+
+void cmBinUtilsLinker::SetError(const std::string& e)
+{
+  this->Archive->SetError(e);
+}
diff --git a/Source/cmBinUtilsLinker.h b/Source/cmBinUtilsLinker.h
new file mode 100644
index 0000000..29853a5
--- /dev/null
+++ b/Source/cmBinUtilsLinker.h
@@ -0,0 +1,30 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#ifndef cmBinUtilsLinker_h
+#define cmBinUtilsLinker_h
+
+#include "cmStateTypes.h"
+
+#include <string>
+
+class cmRuntimeDependencyArchive;
+
+class cmBinUtilsLinker
+{
+public:
+  cmBinUtilsLinker(cmRuntimeDependencyArchive* archive);
+  virtual ~cmBinUtilsLinker() = default;
+
+  virtual bool Prepare() { return true; }
+
+  virtual bool ScanDependencies(std::string const& file,
+                                cmStateEnums::TargetType type) = 0;
+
+protected:
+  cmRuntimeDependencyArchive* Archive;
+
+  void SetError(const std::string& e);
+};
+
+#endif // cmBinUtilsLinker_h
diff --git a/Source/cmBinUtilsLinuxELFGetRuntimeDependenciesTool.cxx b/Source/cmBinUtilsLinuxELFGetRuntimeDependenciesTool.cxx
new file mode 100644
index 0000000..40de592
--- /dev/null
+++ b/Source/cmBinUtilsLinuxELFGetRuntimeDependenciesTool.cxx
@@ -0,0 +1,18 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#include "cmBinUtilsLinuxELFGetRuntimeDependenciesTool.h"
+#include "cmRuntimeDependencyArchive.h"
+
+cmBinUtilsLinuxELFGetRuntimeDependenciesTool::
+  cmBinUtilsLinuxELFGetRuntimeDependenciesTool(
+    cmRuntimeDependencyArchive* archive)
+  : Archive(archive)
+{
+}
+
+void cmBinUtilsLinuxELFGetRuntimeDependenciesTool::SetError(
+  const std::string& error)
+{
+  this->Archive->SetError(error);
+}
diff --git a/Source/cmBinUtilsLinuxELFGetRuntimeDependenciesTool.h b/Source/cmBinUtilsLinuxELFGetRuntimeDependenciesTool.h
new file mode 100644
index 0000000..d514e7f
--- /dev/null
+++ b/Source/cmBinUtilsLinuxELFGetRuntimeDependenciesTool.h
@@ -0,0 +1,30 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#ifndef cmBinUtilsLinuxELFGetRuntimeDependenciesTool_h
+#define cmBinUtilsLinuxELFGetRuntimeDependenciesTool_h
+
+#include <string>
+#include <vector>
+
+class cmRuntimeDependencyArchive;
+
+class cmBinUtilsLinuxELFGetRuntimeDependenciesTool
+{
+public:
+  cmBinUtilsLinuxELFGetRuntimeDependenciesTool(
+    cmRuntimeDependencyArchive* archive);
+  virtual ~cmBinUtilsLinuxELFGetRuntimeDependenciesTool() = default;
+
+  virtual bool GetFileInfo(std::string const& file,
+                           std::vector<std::string>& needed,
+                           std::vector<std::string>& rpaths,
+                           std::vector<std::string>& runpaths) = 0;
+
+protected:
+  cmRuntimeDependencyArchive* Archive;
+
+  void SetError(const std::string& e);
+};
+
+#endif // cmBinUtilsLinuxELFGetRuntimeDependenciesTool_h
diff --git a/Source/cmBinUtilsLinuxELFLinker.cxx b/Source/cmBinUtilsLinuxELFLinker.cxx
new file mode 100644
index 0000000..4fb15f2
--- /dev/null
+++ b/Source/cmBinUtilsLinuxELFLinker.cxx
@@ -0,0 +1,177 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#include "cmBinUtilsLinuxELFLinker.h"
+#include "cmAlgorithms.h"
+#include "cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.h"
+#include "cmLDConfigLDConfigTool.h"
+#include "cmMakefile.h"
+#include "cmMessageType.h"
+#include "cmRuntimeDependencyArchive.h"
+#include "cmSystemTools.h"
+
+#include <cmsys/RegularExpression.hxx>
+
+#include <memory>
+#include <sstream>
+
+static std::string ReplaceOrigin(const std::string& rpath,
+                                 const std::string& origin)
+{
+  static const cmsys::RegularExpression originRegex(
+    "(\\$ORIGIN)([^a-zA-Z0-9_]|$)");
+  static const cmsys::RegularExpression originCurlyRegex("\\${ORIGIN}");
+
+  cmsys::RegularExpressionMatch match;
+  if (originRegex.find(rpath.c_str(), match)) {
+    std::string begin = rpath.substr(0, match.start(1));
+    std::string end = rpath.substr(match.end(1));
+    return begin + origin + end;
+  }
+  if (originCurlyRegex.find(rpath.c_str(), match)) {
+    std::string begin = rpath.substr(0, match.start());
+    std::string end = rpath.substr(match.end());
+    return begin + origin + end;
+  }
+  return rpath;
+}
+
+cmBinUtilsLinuxELFLinker::cmBinUtilsLinuxELFLinker(
+  cmRuntimeDependencyArchive* archive)
+  : cmBinUtilsLinker(archive)
+{
+}
+
+bool cmBinUtilsLinuxELFLinker::Prepare()
+{
+  std::string tool = this->Archive->GetGetRuntimeDependenciesTool();
+  if (tool.empty()) {
+    tool = "objdump";
+  }
+  if (tool == "objdump") {
+    this->Tool =
+      cm::make_unique<cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool>(
+        this->Archive);
+  } else {
+    std::ostringstream e;
+    e << "Invalid value for CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL: " << tool;
+    this->SetError(e.str());
+    return false;
+  }
+
+  std::string ldConfigTool =
+    this->Archive->GetMakefile()->GetSafeDefinition("CMAKE_LDCONFIG_TOOL");
+  if (ldConfigTool.empty()) {
+    ldConfigTool = "ldconfig";
+  }
+  if (ldConfigTool == "ldconfig") {
+    this->LDConfigTool =
+      cm::make_unique<cmLDConfigLDConfigTool>(this->Archive);
+  } else {
+    std::ostringstream e;
+    e << "Invalid value for CMAKE_LDCONFIG_TOOL: " << ldConfigTool;
+    this->SetError(e.str());
+    return false;
+  }
+
+  return true;
+}
+
+bool cmBinUtilsLinuxELFLinker::ScanDependencies(
+  std::string const& file, cmStateEnums::TargetType /* unused */)
+{
+  std::vector<std::string> parentRpaths;
+  return this->ScanDependencies(file, parentRpaths);
+}
+
+bool cmBinUtilsLinuxELFLinker::ScanDependencies(
+  std::string const& file, std::vector<std::string> const& parentRpaths)
+{
+  std::string origin = cmSystemTools::GetFilenamePath(file);
+  std::vector<std::string> needed;
+  std::vector<std::string> rpaths;
+  std::vector<std::string> runpaths;
+  if (!this->Tool->GetFileInfo(file, needed, rpaths, runpaths)) {
+    return false;
+  }
+  for (auto& runpath : runpaths) {
+    runpath = ReplaceOrigin(runpath, origin);
+  }
+  for (auto& rpath : rpaths) {
+    rpath = ReplaceOrigin(rpath, origin);
+  }
+
+  std::vector<std::string> searchPaths;
+  if (!runpaths.empty()) {
+    searchPaths = runpaths;
+  } else {
+    searchPaths = rpaths;
+    searchPaths.insert(searchPaths.end(), parentRpaths.begin(),
+                       parentRpaths.end());
+  }
+
+  std::vector<std::string> ldConfigPaths;
+  if (!this->LDConfigTool->GetLDConfigPaths(ldConfigPaths)) {
+    return false;
+  }
+  searchPaths.insert(searchPaths.end(), ldConfigPaths.begin(),
+                     ldConfigPaths.end());
+
+  for (auto const& dep : needed) {
+    if (!this->Archive->IsPreExcluded(dep)) {
+      std::string path;
+      bool resolved = false;
+      if (dep.find('/') != std::string::npos) {
+        this->SetError("Paths to dependencies are not supported");
+        return false;
+      }
+      if (!this->ResolveDependency(dep, searchPaths, path, resolved)) {
+        return false;
+      }
+      if (resolved) {
+        if (!this->Archive->IsPostExcluded(path)) {
+          bool unique;
+          this->Archive->AddResolvedPath(dep, path, unique);
+          if (unique && !this->ScanDependencies(path, rpaths)) {
+            return false;
+          }
+        }
+      } else {
+        this->Archive->AddUnresolvedPath(dep);
+      }
+    }
+  }
+
+  return true;
+}
+
+bool cmBinUtilsLinuxELFLinker::ResolveDependency(
+  std::string const& name, std::vector<std::string> const& searchPaths,
+  std::string& path, bool& resolved)
+{
+  for (auto const& searchPath : searchPaths) {
+    path = searchPath + '/' + name;
+    if (cmSystemTools::PathExists(path)) {
+      resolved = true;
+      return true;
+    }
+  }
+
+  for (auto const& searchPath : this->Archive->GetSearchDirectories()) {
+    path = searchPath + '/' + name;
+    if (cmSystemTools::PathExists(path)) {
+      std::ostringstream warning;
+      warning << "Dependency " << name << " found in search directory:\n  "
+              << searchPath
+              << "\nSee file(GET_RUNTIME_DEPENDENCIES) documentation for "
+              << "more information.";
+      this->Archive->GetMakefile()->IssueMessage(MessageType::WARNING,
+                                                 warning.str());
+      resolved = true;
+      return true;
+    }
+  }
+
+  resolved = false;
+  return true;
+}
diff --git a/Source/cmBinUtilsLinuxELFLinker.h b/Source/cmBinUtilsLinuxELFLinker.h
new file mode 100644
index 0000000..348edc4
--- /dev/null
+++ b/Source/cmBinUtilsLinuxELFLinker.h
@@ -0,0 +1,44 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#ifndef cmBinUtilsLinuxELFLinker_h
+#define cmBinUtilsLinuxELFLinker_h
+
+#include "cmBinUtilsLinker.h"
+#include "cmBinUtilsLinuxELFGetRuntimeDependenciesTool.h"
+#include "cmLDConfigTool.h"
+#include "cmStateTypes.h"
+
+#include <memory> // IWYU pragma: keep
+#include <string>
+#include <vector>
+
+class cmRuntimeDependencyArchive;
+
+class cmBinUtilsLinuxELFLinker : public cmBinUtilsLinker
+{
+public:
+  cmBinUtilsLinuxELFLinker(cmRuntimeDependencyArchive* archive);
+
+  bool Prepare() override;
+
+  bool ScanDependencies(std::string const& file,
+                        cmStateEnums::TargetType type) override;
+
+private:
+  std::unique_ptr<cmBinUtilsLinuxELFGetRuntimeDependenciesTool> Tool;
+  std::unique_ptr<cmLDConfigTool> LDConfigTool;
+  bool HaveLDConfigPaths = false;
+  std::vector<std::string> LDConfigPaths;
+
+  bool ScanDependencies(std::string const& file,
+                        std::vector<std::string> const& parentRpaths);
+
+  bool ResolveDependency(std::string const& name,
+                         std::vector<std::string> const& searchPaths,
+                         std::string& path, bool& resolved);
+
+  bool GetLDConfigPaths();
+};
+
+#endif // cmBinUtilsLinuxELFLinker_h
diff --git a/Source/cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.cxx b/Source/cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.cxx
new file mode 100644
index 0000000..3bf7bf8
--- /dev/null
+++ b/Source/cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.cxx
@@ -0,0 +1,84 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#include "cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.h"
+#include "cmRuntimeDependencyArchive.h"
+#include "cmSystemTools.h"
+#include "cmUVProcessChain.h"
+
+#include <cmsys/RegularExpression.hxx>
+
+#include <sstream>
+
+cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool::
+  cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool(
+    cmRuntimeDependencyArchive* archive)
+  : cmBinUtilsLinuxELFGetRuntimeDependenciesTool(archive)
+{
+}
+
+bool cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool::GetFileInfo(
+  std::string const& file, std::vector<std::string>& needed,
+  std::vector<std::string>& rpaths, std::vector<std::string>& runpaths)
+{
+  cmUVProcessChainBuilder builder;
+  builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT);
+
+  std::vector<std::string> command;
+  if (!this->Archive->GetGetRuntimeDependenciesCommand("objdump", command)) {
+    this->SetError("Could not find objdump");
+    return false;
+  }
+  command.emplace_back("-p");
+  command.push_back(file);
+  builder.AddCommand(command);
+
+  auto process = builder.Start();
+  if (!process.Valid()) {
+    std::ostringstream e;
+    e << "Failed to start objdump process for:\n  " << file;
+    this->SetError(e.str());
+    return false;
+  }
+
+  std::string line;
+  static const cmsys::RegularExpression neededRegex("^ *NEEDED *([^\n]*)$");
+  static const cmsys::RegularExpression rpathRegex("^ *RPATH *([^\n]*)$");
+  static const cmsys::RegularExpression runpathRegex("^ *RUNPATH *([^\n]*)$");
+  while (std::getline(*process.OutputStream(), line)) {
+    cmsys::RegularExpressionMatch match;
+    if (neededRegex.find(line.c_str(), match)) {
+      needed.push_back(match.match(1));
+    } else if (rpathRegex.find(line.c_str(), match)) {
+      std::vector<std::string> rpathSplit =
+        cmSystemTools::SplitString(match.match(1), ':');
+      rpaths.reserve(rpaths.size() + rpathSplit.size());
+      for (auto const& rpath : rpathSplit) {
+        rpaths.push_back(rpath);
+      }
+    } else if (runpathRegex.find(line.c_str(), match)) {
+      std::vector<std::string> runpathSplit =
+        cmSystemTools::SplitString(match.match(1), ':');
+      runpaths.reserve(runpaths.size() + runpathSplit.size());
+      for (auto const& runpath : runpathSplit) {
+        runpaths.push_back(runpath);
+      }
+    }
+  }
+
+  if (!process.Wait()) {
+    std::ostringstream e;
+    e << "Failed to wait on objdump process for:\n  " << file;
+    this->SetError(e.str());
+    return false;
+  }
+  auto status = process.GetStatus();
+  if (!status[0] || status[0]->ExitStatus != 0) {
+    std::ostringstream e;
+    e << "Failed to run objdump on:\n  " << file;
+    this->SetError(e.str());
+    return false;
+  }
+
+  return true;
+}
diff --git a/Source/cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.h b/Source/cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.h
new file mode 100644
index 0000000..286337f
--- /dev/null
+++ b/Source/cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.h
@@ -0,0 +1,26 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#ifndef cmBinUtilsLinuxELFGetRuntimeCollectDependenciesTool_h
+#define cmBinUtilsLinuxELFGetRuntimeCollectDependenciesTool_h
+
+#include "cmBinUtilsLinuxELFGetRuntimeDependenciesTool.h"
+
+#include <string>
+#include <vector>
+
+class cmRuntimeDependencyArchive;
+
+class cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool
+  : public cmBinUtilsLinuxELFGetRuntimeDependenciesTool
+{
+public:
+  cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool(
+    cmRuntimeDependencyArchive* archive);
+
+  bool GetFileInfo(std::string const& file, std::vector<std::string>& needed,
+                   std::vector<std::string>& rpaths,
+                   std::vector<std::string>& runpaths) override;
+};
+
+#endif // cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool_h
diff --git a/Source/cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.cxx b/Source/cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.cxx
new file mode 100644
index 0000000..a296a47
--- /dev/null
+++ b/Source/cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.cxx
@@ -0,0 +1,19 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#include "cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.h"
+
+#include "cmRuntimeDependencyArchive.h"
+
+cmBinUtilsMacOSMachOGetRuntimeDependenciesTool::
+  cmBinUtilsMacOSMachOGetRuntimeDependenciesTool(
+    cmRuntimeDependencyArchive* archive)
+  : Archive(archive)
+{
+}
+
+void cmBinUtilsMacOSMachOGetRuntimeDependenciesTool::SetError(
+  const std::string& error)
+{
+  this->Archive->SetError(error);
+}
diff --git a/Source/cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.h b/Source/cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.h
new file mode 100644
index 0000000..dbb2882
--- /dev/null
+++ b/Source/cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.h
@@ -0,0 +1,29 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#ifndef cmBinUtilsMacOSMachOGetRuntimeDependenciesTool_h
+#define cmBinUtilsMacOSMachOGetRuntimeDependenciesTool_h
+
+#include <string>
+#include <vector>
+
+class cmRuntimeDependencyArchive;
+
+class cmBinUtilsMacOSMachOGetRuntimeDependenciesTool
+{
+public:
+  cmBinUtilsMacOSMachOGetRuntimeDependenciesTool(
+    cmRuntimeDependencyArchive* archive);
+  virtual ~cmBinUtilsMacOSMachOGetRuntimeDependenciesTool() = default;
+
+  virtual bool GetFileInfo(std::string const& file,
+                           std::vector<std::string>& libs,
+                           std::vector<std::string>& rpaths) = 0;
+
+protected:
+  cmRuntimeDependencyArchive* Archive;
+
+  void SetError(const std::string& error);
+};
+
+#endif // cmBinUtilsMacOSMachOGetRuntimeDependenciesTool_h
diff --git a/Source/cmBinUtilsMacOSMachOLinker.cxx b/Source/cmBinUtilsMacOSMachOLinker.cxx
new file mode 100644
index 0000000..e219847
--- /dev/null
+++ b/Source/cmBinUtilsMacOSMachOLinker.cxx
@@ -0,0 +1,228 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#include "cmBinUtilsMacOSMachOLinker.h"
+
+#include "cmAlgorithms.h"
+#include "cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.h"
+#include "cmRuntimeDependencyArchive.h"
+#include "cmSystemTools.h"
+
+#include <sstream>
+#include <string>
+#include <vector>
+
+cmBinUtilsMacOSMachOLinker::cmBinUtilsMacOSMachOLinker(
+  cmRuntimeDependencyArchive* archive)
+  : cmBinUtilsLinker(archive)
+{
+}
+
+bool cmBinUtilsMacOSMachOLinker::Prepare()
+{
+  std::string tool = this->Archive->GetGetRuntimeDependenciesTool();
+  if (tool.empty()) {
+    tool = "otool";
+  }
+  if (tool == "otool") {
+    this->Tool =
+      cm::make_unique<cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool>(
+        this->Archive);
+  } else {
+    std::ostringstream e;
+    e << "Invalid value for CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL: " << tool;
+    this->SetError(e.str());
+    return false;
+  }
+
+  return true;
+}
+
+bool cmBinUtilsMacOSMachOLinker::ScanDependencies(
+  std::string const& file, cmStateEnums::TargetType type)
+{
+  std::string executableFile;
+  if (type == cmStateEnums::EXECUTABLE) {
+    executableFile = file;
+  } else {
+    executableFile = this->Archive->GetBundleExecutable();
+  }
+  std::string executablePath;
+  if (!executableFile.empty()) {
+    executablePath = cmSystemTools::GetFilenamePath(executableFile);
+  }
+  return this->ScanDependencies(file, executablePath);
+}
+
+bool cmBinUtilsMacOSMachOLinker::ScanDependencies(
+  std::string const& file, std::string const& executablePath)
+{
+  std::vector<std::string> libs, rpaths;
+  if (!this->Tool->GetFileInfo(file, libs, rpaths)) {
+    return false;
+  }
+
+  std::string loaderPath = cmSystemTools::GetFilenamePath(file);
+  return this->GetFileDependencies(libs, executablePath, loaderPath, rpaths);
+}
+
+bool cmBinUtilsMacOSMachOLinker::GetFileDependencies(
+  std::vector<std::string> const& names, std::string const& executablePath,
+  std::string const& loaderPath, std::vector<std::string> const& rpaths)
+{
+  for (std::string const& name : names) {
+    if (!this->Archive->IsPreExcluded(name)) {
+      std::string path;
+      bool resolved;
+      if (!this->ResolveDependency(name, executablePath, loaderPath, rpaths,
+                                   path, resolved)) {
+        return false;
+      }
+      if (resolved) {
+        if (!this->Archive->IsPostExcluded(path)) {
+          auto filename = cmSystemTools::GetFilenameName(path);
+          bool unique;
+          this->Archive->AddResolvedPath(filename, path, unique);
+          if (unique && !this->ScanDependencies(path, executablePath)) {
+            return false;
+          }
+        }
+      } else {
+        this->Archive->AddUnresolvedPath(name);
+      }
+    }
+  }
+
+  return true;
+}
+
+bool cmBinUtilsMacOSMachOLinker::ResolveDependency(
+  std::string const& name, std::string const& executablePath,
+  std::string const& loaderPath, std::vector<std::string> const& rpaths,
+  std::string& path, bool& resolved)
+{
+  resolved = false;
+  if (cmHasLiteralPrefix(name, "@rpath/")) {
+    if (!this->ResolveRPathDependency(name, executablePath, loaderPath, rpaths,
+                                      path, resolved)) {
+      return false;
+    }
+  } else if (cmHasLiteralPrefix(name, "@loader_path/")) {
+    if (!this->ResolveLoaderPathDependency(name, loaderPath, path, resolved)) {
+      return false;
+    }
+  } else if (cmHasLiteralPrefix(name, "@executable_path/")) {
+    if (!this->ResolveExecutablePathDependency(name, executablePath, path,
+                                               resolved)) {
+      return false;
+    }
+  } else {
+    resolved = true;
+    path = name;
+  }
+
+  if (resolved && !cmSystemTools::FileIsFullPath(path)) {
+    this->SetError("Resolved path is not absolute");
+    return false;
+  }
+
+  return true;
+}
+
+bool cmBinUtilsMacOSMachOLinker::ResolveExecutablePathDependency(
+  std::string const& name, std::string const& executablePath,
+  std::string& path, bool& resolved)
+{
+  if (executablePath.empty()) {
+    resolved = false;
+    return true;
+  }
+
+  // 16 is == "@executable_path".length()
+  path = name;
+  path.replace(0, 16, executablePath);
+
+  if (!cmSystemTools::PathExists(path)) {
+    resolved = false;
+    return true;
+  }
+
+  resolved = true;
+  return true;
+}
+
+bool cmBinUtilsMacOSMachOLinker::ResolveLoaderPathDependency(
+  std::string const& name, std::string const& loaderPath, std::string& path,
+  bool& resolved)
+{
+  if (loaderPath.empty()) {
+    resolved = false;
+    return true;
+  }
+
+  // 12 is "@loader_path".length();
+  path = name;
+  path.replace(0, 12, loaderPath);
+
+  if (!cmSystemTools::PathExists(path)) {
+    resolved = false;
+    return true;
+  }
+
+  resolved = true;
+  return true;
+}
+
+bool cmBinUtilsMacOSMachOLinker::ResolveRPathDependency(
+  std::string const& name, std::string const& executablePath,
+  std::string const& loaderPath, std::vector<std::string> const& rpaths,
+  std::string& path, bool& resolved)
+{
+  for (std::string const& rpath : rpaths) {
+    std::string searchFile = name;
+    searchFile.replace(0, 6, rpath);
+    if (cmHasLiteralPrefix(searchFile, "@loader_path/")) {
+      if (!this->ResolveLoaderPathDependency(searchFile, loaderPath, path,
+                                             resolved)) {
+        return false;
+      }
+      if (resolved) {
+        return true;
+      }
+    } else if (cmHasLiteralPrefix(searchFile, "@executable_path/")) {
+      if (!this->ResolveExecutablePathDependency(searchFile, executablePath,
+                                                 path, resolved)) {
+        return false;
+      }
+      if (resolved) {
+        return true;
+      }
+    } else if (cmSystemTools::PathExists(searchFile)) {
+      /*
+       * paraphrasing @ben.boeckel:
+       *  if /b/libB.dylib is supposed to be used,
+       *  /a/libbB.dylib will be found first if it exists. CMake tries to
+       *  sort rpath directories to avoid this, but sometimes there is no
+       *  right answer.
+       *
+       *  I believe it is possible to resolve this using otools -l
+       *  then checking the LC_LOAD_DYLIB command whose name is
+       *  equal to the value of search_file, UNLESS the build
+       *  specifically sets the RPath to paths that will match
+       *  duplicate libs; at this point can we just point to
+       *  user error, or is there a reason why the advantages
+       *  to this scenario outweigh its disadvantages?
+       *
+       *  Also priority seems to be the order as passed in when compiled
+       *  so as long as this method's resolution guarantees priority
+       *  in that manner further checking should not be necessary?
+       */
+      path = searchFile;
+      resolved = true;
+      return true;
+    }
+  }
+
+  resolved = false;
+  return true;
+}
diff --git a/Source/cmBinUtilsMacOSMachOLinker.h b/Source/cmBinUtilsMacOSMachOLinker.h
new file mode 100644
index 0000000..0350d1e
--- /dev/null
+++ b/Source/cmBinUtilsMacOSMachOLinker.h
@@ -0,0 +1,59 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#ifndef cmBinUtilsMacOSMachOLinker_h
+#define cmBinUtilsMacOSMachOLinker_h
+
+#include "cmBinUtilsLinker.h"
+#include "cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.h"
+#include "cmStateTypes.h"
+
+#include <memory> // IWYU pragma: keep
+#include <string>
+#include <vector>
+
+class cmRuntimeDependencyArchive;
+
+class cmBinUtilsMacOSMachOLinker : public cmBinUtilsLinker
+{
+public:
+  cmBinUtilsMacOSMachOLinker(cmRuntimeDependencyArchive* archive);
+
+  bool Prepare() override;
+
+  bool ScanDependencies(std::string const& file,
+                        cmStateEnums::TargetType type) override;
+
+private:
+  std::unique_ptr<cmBinUtilsMacOSMachOGetRuntimeDependenciesTool> Tool;
+
+  bool ScanDependencies(std::string const& file,
+                        std::string const& executablePath);
+
+  bool GetFileDependencies(std::vector<std::string> const& names,
+                           std::string const& executablePath,
+                           std::string const& loaderPath,
+                           std::vector<std::string> const& rpaths);
+
+  bool ResolveDependency(std::string const& name,
+                         std::string const& executablePath,
+                         std::string const& loaderPath,
+                         std::vector<std::string> const& rpaths,
+                         std::string& path, bool& resolved);
+
+  bool ResolveExecutablePathDependency(std::string const& name,
+                                       std::string const& executablePath,
+                                       std::string& path, bool& resolved);
+
+  bool ResolveLoaderPathDependency(std::string const& name,
+                                   std::string const& loaderPath,
+                                   std::string& path, bool& resolved);
+
+  bool ResolveRPathDependency(std::string const& name,
+                              std::string const& executablePath,
+                              std::string const& loaderPath,
+                              std::vector<std::string> const& rpaths,
+                              std::string& path, bool& resolved);
+};
+
+#endif // cmBinUtilsMacOSMachOLinker_h
diff --git a/Source/cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.cxx b/Source/cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.cxx
new file mode 100644
index 0000000..bab2382
--- /dev/null
+++ b/Source/cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.cxx
@@ -0,0 +1,100 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#include "cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.h"
+
+#include "cmRuntimeDependencyArchive.h"
+#include "cmUVProcessChain.h"
+
+#include <cmsys/RegularExpression.hxx>
+
+#include <sstream>
+
+cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool::
+  cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool(
+    cmRuntimeDependencyArchive* archive)
+  : cmBinUtilsMacOSMachOGetRuntimeDependenciesTool(archive)
+{
+}
+
+bool cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool::GetFileInfo(
+  std::string const& file, std::vector<std::string>& libs,
+  std::vector<std::string>& rpaths)
+{
+  std::vector<std::string> command;
+  if (!this->Archive->GetGetRuntimeDependenciesCommand("otool", command)) {
+    this->SetError("Could not find otool");
+    return false;
+  }
+  command.emplace_back("-l");
+  command.emplace_back(file);
+
+  cmUVProcessChainBuilder builder;
+  builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT)
+    .AddCommand(command);
+
+  auto process = builder.Start();
+  if (!process.Valid()) {
+    std::ostringstream e;
+    e << "Failed to start otool process for:\n  " << file;
+    this->SetError(e.str());
+    return false;
+  }
+
+  std::string line;
+  static const cmsys::RegularExpression rpathRegex("^ *cmd LC_RPATH$");
+  static const cmsys::RegularExpression loadDylibRegex(
+    "^ *cmd LC_LOAD_DYLIB$");
+  static const cmsys::RegularExpression pathRegex(
+    "^ *path (.*) \\(offset [0-9]+\\)$");
+  static const cmsys::RegularExpression nameRegex(
+    "^ *name (.*) \\(offset [0-9]+\\)$");
+  while (std::getline(*process.OutputStream(), line)) {
+    cmsys::RegularExpressionMatch cmdMatch;
+    if (rpathRegex.find(line.c_str(), cmdMatch)) {
+      if (!std::getline(*process.OutputStream(), line) ||
+          !std::getline(*process.OutputStream(), line)) {
+        this->SetError("Invalid output from otool");
+        return false;
+      }
+
+      cmsys::RegularExpressionMatch pathMatch;
+      if (pathRegex.find(line.c_str(), pathMatch)) {
+        rpaths.push_back(pathMatch.match(1));
+      } else {
+        this->SetError("Invalid output from otool");
+        return false;
+      }
+    } else if (loadDylibRegex.find(line.c_str(), cmdMatch)) {
+      if (!std::getline(*process.OutputStream(), line) ||
+          !std::getline(*process.OutputStream(), line)) {
+        this->SetError("Invalid output from otool");
+        return false;
+      }
+
+      cmsys::RegularExpressionMatch nameMatch;
+      if (nameRegex.find(line.c_str(), nameMatch)) {
+        libs.push_back(nameMatch.match(1));
+      } else {
+        this->SetError("Invalid output from otool");
+        return false;
+      }
+    }
+  }
+
+  if (!process.Wait()) {
+    std::ostringstream e;
+    e << "Failed to wait on otool process for:\n  " << file;
+    this->SetError(e.str());
+    return false;
+  }
+  auto status = process.GetStatus();
+  if (!status[0] || status[0]->ExitStatus != 0) {
+    std::ostringstream e;
+    e << "Failed to run otool on:\n  " << file;
+    this->SetError(e.str());
+    return false;
+  }
+
+  return true;
+}
diff --git a/Source/cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.h b/Source/cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.h
new file mode 100644
index 0000000..12bcbc1
--- /dev/null
+++ b/Source/cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.h
@@ -0,0 +1,25 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#ifndef cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool_h
+#define cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool_h
+
+#include "cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.h"
+
+#include <string>
+#include <vector>
+
+class cmRuntimeDependencyArchive;
+
+class cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool
+  : public cmBinUtilsMacOSMachOGetRuntimeDependenciesTool
+{
+public:
+  cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool(
+    cmRuntimeDependencyArchive* archive);
+
+  bool GetFileInfo(std::string const& file, std::vector<std::string>& libs,
+                   std::vector<std::string>& rpaths) override;
+};
+
+#endif // cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool_h
diff --git a/Source/cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.cxx b/Source/cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.cxx
new file mode 100644
index 0000000..2b35e30
--- /dev/null
+++ b/Source/cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.cxx
@@ -0,0 +1,67 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#include "cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.h"
+#include "cmRuntimeDependencyArchive.h"
+#include "cmUVProcessChain.h"
+
+#include <cmsys/RegularExpression.hxx>
+
+#include <sstream>
+
+cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool::
+  cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool(
+    cmRuntimeDependencyArchive* archive)
+  : cmBinUtilsWindowsPEGetRuntimeDependenciesTool(archive)
+{
+}
+
+bool cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool::GetFileInfo(
+  const std::string& file, std::vector<std::string>& needed)
+{
+  cmUVProcessChainBuilder builder;
+  builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT);
+
+  std::vector<std::string> command;
+  if (!this->Archive->GetGetRuntimeDependenciesCommand("dumpbin", command)) {
+    this->SetError("Could not find dumpbin");
+    return false;
+  }
+  command.emplace_back("/dependents");
+  command.push_back(file);
+  builder.AddCommand(command);
+
+  auto process = builder.Start();
+  if (!process.Valid()) {
+    std::ostringstream e;
+    e << "Failed to start dumpbin process for:\n  " << file;
+    this->SetError(e.str());
+    return false;
+  }
+
+  std::string line;
+  static const cmsys::RegularExpression regex(
+    "^    ([^\n]*\\.[Dd][Ll][Ll])\r$");
+  while (std::getline(*process.OutputStream(), line)) {
+    cmsys::RegularExpressionMatch match;
+    if (regex.find(line.c_str(), match)) {
+      needed.push_back(match.match(1));
+    }
+  }
+
+  if (!process.Wait()) {
+    std::ostringstream e;
+    e << "Failed to wait on dumpbin process for:\n  " << file;
+    this->SetError(e.str());
+    return false;
+  }
+  auto status = process.GetStatus();
+  if (!status[0] || status[0]->ExitStatus != 0) {
+    std::ostringstream e;
+    e << "Failed to run dumpbin on:\n  " << file;
+    this->SetError(e.str());
+    return false;
+  }
+
+  return true;
+}
diff --git a/Source/cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.h b/Source/cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.h
new file mode 100644
index 0000000..4c17f8d
--- /dev/null
+++ b/Source/cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.h
@@ -0,0 +1,25 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#ifndef cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool_h
+#define cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool_h
+
+#include "cmBinUtilsWindowsPEGetRuntimeDependenciesTool.h"
+
+#include <string>
+#include <vector>
+
+class cmRuntimeDependencyArchive;
+
+class cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool
+  : public cmBinUtilsWindowsPEGetRuntimeDependenciesTool
+{
+public:
+  cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool(
+    cmRuntimeDependencyArchive* archive);
+
+  bool GetFileInfo(const std::string& file,
+                   std::vector<std::string>& needed) override;
+};
+
+#endif // cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool_h
diff --git a/Source/cmBinUtilsWindowsPEGetRuntimeDependenciesTool.cxx b/Source/cmBinUtilsWindowsPEGetRuntimeDependenciesTool.cxx
new file mode 100644
index 0000000..f5a4431
--- /dev/null
+++ b/Source/cmBinUtilsWindowsPEGetRuntimeDependenciesTool.cxx
@@ -0,0 +1,18 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#include "cmBinUtilsWindowsPEGetRuntimeDependenciesTool.h"
+#include "cmRuntimeDependencyArchive.h"
+
+cmBinUtilsWindowsPEGetRuntimeDependenciesTool::
+  cmBinUtilsWindowsPEGetRuntimeDependenciesTool(
+    cmRuntimeDependencyArchive* archive)
+  : Archive(archive)
+{
+}
+
+void cmBinUtilsWindowsPEGetRuntimeDependenciesTool::SetError(
+  const std::string& error)
+{
+  this->Archive->SetError(error);
+}
diff --git a/Source/cmBinUtilsWindowsPEGetRuntimeDependenciesTool.h b/Source/cmBinUtilsWindowsPEGetRuntimeDependenciesTool.h
new file mode 100644
index 0000000..e9e402b
--- /dev/null
+++ b/Source/cmBinUtilsWindowsPEGetRuntimeDependenciesTool.h
@@ -0,0 +1,28 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#ifndef cmBinUtilsWindowsPEGetRuntimeDependenciesTool_h
+#define cmBinUtilsWindowsPEGetRuntimeDependenciesTool_h
+
+#include <string>
+#include <vector>
+
+class cmRuntimeDependencyArchive;
+
+class cmBinUtilsWindowsPEGetRuntimeDependenciesTool
+{
+public:
+  cmBinUtilsWindowsPEGetRuntimeDependenciesTool(
+    cmRuntimeDependencyArchive* archive);
+  virtual ~cmBinUtilsWindowsPEGetRuntimeDependenciesTool() = default;
+
+  virtual bool GetFileInfo(const std::string& file,
+                           std::vector<std::string>& needed) = 0;
+
+protected:
+  cmRuntimeDependencyArchive* Archive;
+
+  void SetError(const std::string& error);
+};
+
+#endif // cmBinUtilsWindowsPEGetRuntimeDependenciesTool_h
diff --git a/Source/cmBinUtilsWindowsPELinker.cxx b/Source/cmBinUtilsWindowsPELinker.cxx
new file mode 100644
index 0000000..796e9ed
--- /dev/null
+++ b/Source/cmBinUtilsWindowsPELinker.cxx
@@ -0,0 +1,121 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#include "cmBinUtilsWindowsPELinker.h"
+#include "cmAlgorithms.h"
+#include "cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.h"
+#include "cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.h"
+#include "cmRuntimeDependencyArchive.h"
+#include "cmSystemTools.h"
+
+#include <memory>
+#include <sstream>
+#include <vector>
+
+#ifdef _WIN32
+#  include <windows.h>
+#endif
+
+cmBinUtilsWindowsPELinker::cmBinUtilsWindowsPELinker(
+  cmRuntimeDependencyArchive* archive)
+  : cmBinUtilsLinker(archive)
+{
+}
+
+bool cmBinUtilsWindowsPELinker::Prepare()
+{
+  std::string tool = this->Archive->GetGetRuntimeDependenciesTool();
+  if (tool.empty()) {
+    std::vector<std::string> command;
+    if (this->Archive->GetGetRuntimeDependenciesCommand("dumpbin", command)) {
+      tool = "dumpbin";
+    } else {
+      tool = "objdump";
+    }
+  }
+  if (tool == "dumpbin") {
+    this->Tool =
+      cm::make_unique<cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool>(
+        this->Archive);
+  } else if (tool == "objdump") {
+    this->Tool =
+      cm::make_unique<cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool>(
+        this->Archive);
+  } else {
+    std::ostringstream e;
+    e << "Invalid value for CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL: " << tool;
+    this->SetError(e.str());
+    return false;
+  }
+
+  return true;
+}
+
+bool cmBinUtilsWindowsPELinker::ScanDependencies(
+  std::string const& file, cmStateEnums::TargetType /* unused */)
+{
+  std::vector<std::string> needed;
+  if (!this->Tool->GetFileInfo(file, needed)) {
+    return false;
+  }
+  for (auto& n : needed) {
+    n = cmSystemTools::LowerCase(n);
+  }
+  std::string origin = cmSystemTools::GetFilenamePath(file);
+
+  for (auto const& lib : needed) {
+    if (!this->Archive->IsPreExcluded(lib)) {
+      std::string path;
+      bool resolved = false;
+      if (!this->ResolveDependency(lib, origin, path, resolved)) {
+        return false;
+      }
+      if (resolved) {
+        if (!this->Archive->IsPostExcluded(path)) {
+          bool unique;
+          this->Archive->AddResolvedPath(lib, path, unique);
+          if (unique &&
+              !this->ScanDependencies(path, cmStateEnums::SHARED_LIBRARY)) {
+            return false;
+          }
+        }
+      } else {
+        this->Archive->AddUnresolvedPath(lib);
+      }
+    }
+  }
+
+  return true;
+}
+
+bool cmBinUtilsWindowsPELinker::ResolveDependency(std::string const& name,
+                                                  std::string const& origin,
+                                                  std::string& path,
+                                                  bool& resolved)
+{
+  auto dirs = this->Archive->GetSearchDirectories();
+
+#ifdef _WIN32
+  char buf[MAX_PATH];
+  unsigned int len;
+  if ((len = GetWindowsDirectoryA(buf, MAX_PATH)) > 0) {
+    dirs.insert(dirs.begin(), std::string(buf, len));
+  }
+  if ((len = GetSystemDirectoryA(buf, MAX_PATH)) > 0) {
+    dirs.insert(dirs.begin(), std::string(buf, len));
+  }
+#endif
+
+  dirs.insert(dirs.begin(), origin);
+
+  for (auto const& searchPath : dirs) {
+    path = searchPath + '/' + name;
+    if (cmSystemTools::PathExists(path)) {
+      resolved = true;
+      return true;
+    }
+  }
+
+  resolved = false;
+  return true;
+}
diff --git a/Source/cmBinUtilsWindowsPELinker.h b/Source/cmBinUtilsWindowsPELinker.h
new file mode 100644
index 0000000..d742195
--- /dev/null
+++ b/Source/cmBinUtilsWindowsPELinker.h
@@ -0,0 +1,33 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#ifndef cmBinUtilsWindowsPELinker_h
+#define cmBinUtilsWindowsPELinker_h
+
+#include "cmBinUtilsLinker.h"
+#include "cmBinUtilsWindowsPEGetRuntimeDependenciesTool.h"
+#include "cmStateTypes.h"
+
+#include <memory> // IWYU pragma: keep
+#include <string>
+
+class cmRuntimeDependencyArchive;
+
+class cmBinUtilsWindowsPELinker : public cmBinUtilsLinker
+{
+public:
+  cmBinUtilsWindowsPELinker(cmRuntimeDependencyArchive* archive);
+
+  bool Prepare() override;
+
+  bool ScanDependencies(std::string const& file,
+                        cmStateEnums::TargetType type) override;
+
+private:
+  std::unique_ptr<cmBinUtilsWindowsPEGetRuntimeDependenciesTool> Tool;
+
+  bool ResolveDependency(std::string const& name, std::string const& origin,
+                         std::string& path, bool& resolved);
+};
+
+#endif // cmBinUtilsWindowsPELinker_h
diff --git a/Source/cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.cxx b/Source/cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.cxx
new file mode 100644
index 0000000..1f27003
--- /dev/null
+++ b/Source/cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.cxx
@@ -0,0 +1,67 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#include "cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.h"
+#include "cmRuntimeDependencyArchive.h"
+#include "cmUVProcessChain.h"
+
+#include <cmsys/RegularExpression.hxx>
+
+#include <sstream>
+
+cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool::
+  cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool(
+    cmRuntimeDependencyArchive* archive)
+  : cmBinUtilsWindowsPEGetRuntimeDependenciesTool(archive)
+{
+}
+
+bool cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool::GetFileInfo(
+  const std::string& file, std::vector<std::string>& needed)
+{
+  cmUVProcessChainBuilder builder;
+  builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT);
+
+  std::vector<std::string> command;
+  if (!this->Archive->GetGetRuntimeDependenciesCommand("objdump", command)) {
+    this->SetError("Could not find objdump");
+    return false;
+  }
+  command.emplace_back("-p");
+  command.push_back(file);
+  builder.AddCommand(command);
+
+  auto process = builder.Start();
+  if (!process.Valid()) {
+    std::ostringstream e;
+    e << "Failed to start objdump process for:\n  " << file;
+    this->SetError(e.str());
+    return false;
+  }
+
+  std::string line;
+  static const cmsys::RegularExpression regex(
+    "^\t*DLL Name: ([^\n]*\\.[Dd][Ll][Ll])\r$");
+  while (std::getline(*process.OutputStream(), line)) {
+    cmsys::RegularExpressionMatch match;
+    if (regex.find(line.c_str(), match)) {
+      needed.push_back(match.match(1));
+    }
+  }
+
+  if (!process.Wait()) {
+    std::ostringstream e;
+    e << "Failed to wait on objdump process for:\n  " << file;
+    this->SetError(e.str());
+    return false;
+  }
+  auto status = process.GetStatus();
+  if (!status[0] || status[0]->ExitStatus != 0) {
+    std::ostringstream e;
+    e << "Failed to run objdump on:\n  " << file;
+    this->SetError(e.str());
+    return false;
+  }
+
+  return true;
+}
diff --git a/Source/cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.h b/Source/cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.h
new file mode 100644
index 0000000..1d1a5b0
--- /dev/null
+++ b/Source/cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.h
@@ -0,0 +1,25 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#ifndef cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool_h
+#define cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool_h
+
+#include "cmBinUtilsWindowsPEGetRuntimeDependenciesTool.h"
+
+#include <string>
+#include <vector>
+
+class cmRuntimeDependencyArchive;
+
+class cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool
+  : public cmBinUtilsWindowsPEGetRuntimeDependenciesTool
+{
+public:
+  cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool(
+    cmRuntimeDependencyArchive* archive);
+
+  bool GetFileInfo(const std::string& file,
+                   std::vector<std::string>& needed) override;
+};
+
+#endif // cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool_h
diff --git a/Source/cmCacheManager.cxx b/Source/cmCacheManager.cxx
index 358f095..e8fc350 100644
--- a/Source/cmCacheManager.cxx
+++ b/Source/cmCacheManager.cxx
@@ -620,7 +620,7 @@
 
 std::vector<std::string> cmCacheManager::CacheEntry::GetPropertyList() const
 {
-  return this->Properties.GetPropertyList();
+  return this->Properties.GetKeys();
 }
 
 const char* cmCacheManager::CacheEntry::GetProperty(
diff --git a/Source/cmDefinitions.cxx b/Source/cmDefinitions.cxx
index 5fafaf9..894447c 100644
--- a/Source/cmDefinitions.cxx
+++ b/Source/cmDefinitions.cxx
@@ -57,8 +57,7 @@
 
 void cmDefinitions::Set(const std::string& key, const char* value)
 {
-  Def def(value);
-  this->Map[key] = def;
+  this->Map[key] = Def(value);
 }
 
 std::vector<std::string> cmDefinitions::UnusedKeys() const
diff --git a/Source/cmExportFileGenerator.cxx b/Source/cmExportFileGenerator.cxx
index a12e0c4..c366183 100644
--- a/Source/cmExportFileGenerator.cxx
+++ b/Source/cmExportFileGenerator.cxx
@@ -12,7 +12,6 @@
 #include "cmMessageType.h"
 #include "cmOutputConverter.h"
 #include "cmPolicies.h"
-#include "cmProperty.h"
 #include "cmPropertyMap.h"
 #include "cmStateTypes.h"
 #include "cmSystemTools.h"
@@ -1205,12 +1204,9 @@
   std::string& errorMessage)
 {
   auto& targetProperties = gte->Target->GetProperties();
-  const auto& exportProperties = targetProperties.find("EXPORT_PROPERTIES");
-  if (exportProperties != targetProperties.end()) {
-    std::vector<std::string> propsToExport;
-    cmSystemTools::ExpandListArgument(exportProperties->second.GetValue(),
-                                      propsToExport);
-    for (auto& prop : propsToExport) {
+  if (const char* exportProperties =
+        targetProperties.GetPropertyValue("EXPORT_PROPERTIES")) {
+    for (auto& prop : cmSystemTools::ExpandedListArgument(exportProperties)) {
       /* Black list reserved properties */
       if (cmSystemTools::StringStartsWith(prop, "IMPORTED_") ||
           cmSystemTools::StringStartsWith(prop, "INTERFACE_")) {
diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx
index 7a3954e..980ad21 100644
--- a/Source/cmFileCommand.cxx
+++ b/Source/cmFileCommand.cxx
@@ -12,7 +12,9 @@
 #include <assert.h>
 #include <cmath>
 #include <ctype.h>
+#include <map>
 #include <memory> // IWYU pragma: keep
+#include <set>
 #include <sstream>
 #include <stdio.h>
 #include <stdlib.h>
@@ -34,6 +36,8 @@
 #include "cmMessageType.h"
 #include "cmPolicies.h"
 #include "cmRange.h"
+#include "cmRuntimeDependencyArchive.h"
+#include "cmState.h"
 #include "cmSystemTools.h"
 #include "cmTimestamp.h"
 #include "cm_sys_stat.h"
@@ -184,6 +188,9 @@
   if (subCommand == "CREATE_LINK") {
     return this->HandleCreateLinkCommand(args);
   }
+  if (subCommand == "GET_RUNTIME_DEPENDENCIES") {
+    return this->HandleGetRuntimeDependenciesCommand(args);
+  }
 
   std::string e = "does not recognize sub-command " + subCommand;
   this->SetError(e);
@@ -2690,3 +2697,171 @@
 
   return true;
 }
+
+bool cmFileCommand::HandleGetRuntimeDependenciesCommand(
+  std::vector<std::string> const& args)
+{
+  static const std::set<std::string> supportedPlatforms = { "Windows", "Linux",
+                                                            "Darwin" };
+  std::string platform =
+    this->Makefile->GetSafeDefinition("CMAKE_HOST_SYSTEM_NAME");
+  if (!supportedPlatforms.count(platform)) {
+    std::ostringstream e;
+    e << "GET_RUNTIME_DEPENDENCIES is not supported on system \"" << platform
+      << "\"";
+    this->SetError(e.str());
+    cmSystemTools::SetFatalErrorOccured();
+    return false;
+  }
+
+  if (this->Makefile->GetState()->GetMode() == cmState::Project) {
+    this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING,
+                                 "You have used file(GET_RUNTIME_DEPENDENCIES)"
+                                 " in project mode. This is probably not what "
+                                 "you intended to do. Instead, please consider"
+                                 " using it in an install(CODE) or "
+                                 "install(SCRIPT) command. For example:"
+                                 "\n  install(CODE [["
+                                 "\n    file(GET_RUNTIME_DEPENDENCIES"
+                                 "\n      # ..."
+                                 "\n      )"
+                                 "\n    ]])");
+  }
+
+  struct Arguments
+  {
+    std::string ResolvedDependenciesVar;
+    std::string UnresolvedDependenciesVar;
+    std::string ConflictingDependenciesPrefix;
+    std::string BundleExecutable;
+    std::vector<std::string> Executables;
+    std::vector<std::string> Libraries;
+    std::vector<std::string> Directories;
+    std::vector<std::string> Modules;
+    std::vector<std::string> PreIncludeRegexes;
+    std::vector<std::string> PreExcludeRegexes;
+    std::vector<std::string> PostIncludeRegexes;
+    std::vector<std::string> PostExcludeRegexes;
+  };
+
+  static auto const parser =
+    cmArgumentParser<Arguments>{}
+      .Bind("RESOLVED_DEPENDENCIES_VAR"_s, &Arguments::ResolvedDependenciesVar)
+      .Bind("UNRESOLVED_DEPENDENCIES_VAR"_s,
+            &Arguments::UnresolvedDependenciesVar)
+      .Bind("CONFLICTING_DEPENDENCIES_PREFIX"_s,
+            &Arguments::ConflictingDependenciesPrefix)
+      .Bind("BUNDLE_EXECUTABLE"_s, &Arguments::BundleExecutable)
+      .Bind("EXECUTABLES"_s, &Arguments::Executables)
+      .Bind("LIBRARIES"_s, &Arguments::Libraries)
+      .Bind("MODULES"_s, &Arguments::Modules)
+      .Bind("DIRECTORIES"_s, &Arguments::Directories)
+      .Bind("PRE_INCLUDE_REGEXES"_s, &Arguments::PreIncludeRegexes)
+      .Bind("PRE_EXCLUDE_REGEXES"_s, &Arguments::PreExcludeRegexes)
+      .Bind("POST_INCLUDE_REGEXES"_s, &Arguments::PostIncludeRegexes)
+      .Bind("POST_EXCLUDE_REGEXES"_s, &Arguments::PostExcludeRegexes);
+
+  std::vector<std::string> unrecognizedArguments;
+  std::vector<std::string> keywordsMissingValues;
+  auto parsedArgs =
+    parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments,
+                 &keywordsMissingValues);
+  auto argIt = unrecognizedArguments.begin();
+  if (argIt != unrecognizedArguments.end()) {
+    std::ostringstream e;
+    e << "Unrecognized argument: \"" << *argIt << "\"";
+    this->SetError(e.str());
+    cmSystemTools::SetFatalErrorOccured();
+    return false;
+  }
+  argIt = keywordsMissingValues.begin();
+  if (argIt != keywordsMissingValues.end()) {
+    std::ostringstream e;
+    e << "Keyword missing value: " << *argIt;
+    this->SetError(e.str());
+    cmSystemTools::SetFatalErrorOccured();
+    return false;
+  }
+
+  cmRuntimeDependencyArchive archive(
+    this, parsedArgs.Directories, parsedArgs.BundleExecutable,
+    parsedArgs.PreIncludeRegexes, parsedArgs.PreExcludeRegexes,
+    parsedArgs.PostIncludeRegexes, parsedArgs.PostExcludeRegexes);
+  if (!archive.Prepare()) {
+    cmSystemTools::SetFatalErrorOccured();
+    return false;
+  }
+
+  if (!archive.GetRuntimeDependencies(
+        parsedArgs.Executables, parsedArgs.Libraries, parsedArgs.Modules)) {
+    cmSystemTools::SetFatalErrorOccured();
+    return false;
+  }
+
+  std::vector<std::string> deps, unresolvedDeps, conflictingDeps;
+  for (auto const& val : archive.GetResolvedPaths()) {
+    bool unique = true;
+    auto it = val.second.begin();
+    assert(it != val.second.end());
+    auto const& firstPath = *it;
+    while (++it != val.second.end()) {
+      if (!cmSystemTools::SameFile(firstPath, *it)) {
+        unique = false;
+        break;
+      }
+    }
+
+    if (unique) {
+      deps.push_back(firstPath);
+    } else if (!parsedArgs.ConflictingDependenciesPrefix.empty()) {
+      conflictingDeps.push_back(val.first);
+      std::vector<std::string> paths;
+      paths.insert(paths.begin(), val.second.begin(), val.second.end());
+      std::string varName =
+        parsedArgs.ConflictingDependenciesPrefix + "_" + val.first;
+      std::string pathsStr = cmJoin(paths, ";");
+      this->Makefile->AddDefinition(varName, pathsStr.c_str());
+    } else {
+      std::ostringstream e;
+      e << "Multiple conflicting paths found for " << val.first << ":";
+      for (auto const& path : val.second) {
+        e << "\n  " << path;
+      }
+      this->SetError(e.str());
+      cmSystemTools::SetFatalErrorOccured();
+      return false;
+    }
+  }
+  if (!archive.GetUnresolvedPaths().empty()) {
+    if (!parsedArgs.UnresolvedDependenciesVar.empty()) {
+      unresolvedDeps.insert(unresolvedDeps.begin(),
+                            archive.GetUnresolvedPaths().begin(),
+                            archive.GetUnresolvedPaths().end());
+    } else {
+      auto it = archive.GetUnresolvedPaths().begin();
+      assert(it != archive.GetUnresolvedPaths().end());
+      std::ostringstream e;
+      e << "Could not resolve file " << *it;
+      this->SetError(e.str());
+      cmSystemTools::SetFatalErrorOccured();
+      return false;
+    }
+  }
+
+  if (!parsedArgs.ResolvedDependenciesVar.empty()) {
+    std::string val = cmJoin(deps, ";");
+    this->Makefile->AddDefinition(parsedArgs.ResolvedDependenciesVar,
+                                  val.c_str());
+  }
+  if (!parsedArgs.UnresolvedDependenciesVar.empty()) {
+    std::string val = cmJoin(unresolvedDeps, ";");
+    this->Makefile->AddDefinition(parsedArgs.UnresolvedDependenciesVar,
+                                  val.c_str());
+  }
+  if (!parsedArgs.ConflictingDependenciesPrefix.empty()) {
+    std::string val = cmJoin(conflictingDeps, ";");
+    this->Makefile->AddDefinition(
+      parsedArgs.ConflictingDependenciesPrefix + "_FILENAMES", val.c_str());
+  }
+  return true;
+}
diff --git a/Source/cmFileCommand.h b/Source/cmFileCommand.h
index 12c5115..cfff894 100644
--- a/Source/cmFileCommand.h
+++ b/Source/cmFileCommand.h
@@ -62,6 +62,8 @@
   bool HandleSizeCommand(std::vector<std::string> const& args);
   bool HandleReadSymlinkCommand(std::vector<std::string> const& args);
   bool HandleCreateLinkCommand(std::vector<std::string> const& args);
+  bool HandleGetRuntimeDependenciesCommand(
+    std::vector<std::string> const& args);
 
 private:
   void AddEvaluationFile(const std::string& inputName,
diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx
index 036a07d..de0f371 100644
--- a/Source/cmGeneratorTarget.cxx
+++ b/Source/cmGeneratorTarget.cxx
@@ -5032,13 +5032,7 @@
 
 std::vector<std::string> cmGeneratorTarget::GetPropertyKeys() const
 {
-  cmPropertyMap const& propsObject = this->Target->GetProperties();
-  std::vector<std::string> props;
-  props.reserve(propsObject.size());
-  for (auto const& it : propsObject) {
-    props.push_back(it.first);
-  }
-  return props;
+  return this->Target->GetProperties().GetKeys();
 }
 
 void cmGeneratorTarget::ReportPropertyOrigin(
diff --git a/Source/cmInstallTargetGenerator.cxx b/Source/cmInstallTargetGenerator.cxx
index 7c5a55b..0774436 100644
--- a/Source/cmInstallTargetGenerator.cxx
+++ b/Source/cmInstallTargetGenerator.cxx
@@ -16,6 +16,8 @@
 #include "cmLocalGenerator.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
+#include "cmOutputConverter.h"
+#include "cmPolicies.h"
 #include "cmStateTypes.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
@@ -632,17 +634,34 @@
     return;
   }
 
-  // Get the install RPATH from the link information.
-  std::string newRpath = cli->GetChrpathString();
-
   // Write a rule to remove the installed file if its rpath is not the
   // new rpath.  This is needed for existing build/install trees when
   // the installed rpath changes but the file is not rebuilt.
-  /* clang-format off */
   os << indent << "file(RPATH_CHECK\n"
-     << indent << "     FILE \"" << toDestDirPath << "\"\n"
-     << indent << "     RPATH \"" << newRpath << "\")\n";
-  /* clang-format on */
+     << indent << "     FILE \"" << toDestDirPath << "\"\n";
+
+  // CMP0095: ``RPATH`` entries are properly escaped in the intermediary
+  // CMake install script.
+  switch (this->Target->GetPolicyStatusCMP0095()) {
+    case cmPolicies::WARN:
+      // No author warning needed here, we warn later in
+      // cmInstallTargetGenerator::AddChrpathPatchRule().
+      CM_FALLTHROUGH;
+    case cmPolicies::OLD: {
+      // Get the install RPATH from the link information.
+      std::string newRpath = cli->GetChrpathString();
+      os << indent << "     RPATH \"" << newRpath << "\")\n";
+      break;
+    }
+    default: {
+      // Get the install RPATH from the link information and
+      // escape any CMake syntax in the install RPATH.
+      std::string escapedNewRpath =
+        cmOutputConverter::EscapeForCMake(cli->GetChrpathString());
+      os << indent << "     RPATH " << escapedNewRpath << ")\n";
+      break;
+    }
+  }
 }
 
 void cmInstallTargetGenerator::AddChrpathPatchRule(
@@ -731,11 +750,28 @@
       return;
     }
 
+    // Escape any CMake syntax in the RPATHs.
+    std::string escapedOldRpath = cmOutputConverter::EscapeForCMake(oldRpath);
+    std::string escapedNewRpath = cmOutputConverter::EscapeForCMake(newRpath);
+
     // Write a rule to run chrpath to set the install-tree RPATH
     os << indent << "file(RPATH_CHANGE\n"
        << indent << "     FILE \"" << toDestDirPath << "\"\n"
-       << indent << "     OLD_RPATH \"" << oldRpath << "\"\n"
-       << indent << "     NEW_RPATH \"" << newRpath << "\")\n";
+       << indent << "     OLD_RPATH " << escapedOldRpath << "\n";
+
+    // CMP0095: ``RPATH`` entries are properly escaped in the intermediary
+    // CMake install script.
+    switch (this->Target->GetPolicyStatusCMP0095()) {
+      case cmPolicies::WARN:
+        this->IssueCMP0095Warning(newRpath);
+        CM_FALLTHROUGH;
+      case cmPolicies::OLD:
+        os << indent << "     NEW_RPATH \"" << newRpath << "\")\n";
+        break;
+      default:
+        os << indent << "     NEW_RPATH " << escapedNewRpath << ")\n";
+        break;
+    }
   }
 }
 
@@ -838,3 +874,26 @@
      << "\"" << this->Target->Target->GetName() << "\" "
      << "\"" << toDestDirPath << "\")\n";
 }
+
+void cmInstallTargetGenerator::IssueCMP0095Warning(
+  const std::string& unescapedRpath)
+{
+  // Reduce warning noise to cases where used RPATHs may actually be affected
+  // by CMP0095. This filter is meant to skip warnings in cases when
+  // non-curly-braces syntax (e.g. $ORIGIN) or no keyword is used which has
+  // worked already before CMP0095. We intend to issue a warning in all cases
+  // with curly-braces syntax, even if the workaround of double-escaping is in
+  // place, since we deprecate the need for it with CMP0095.
+  const bool potentially_affected(unescapedRpath.find("${") !=
+                                  std::string::npos);
+
+  if (potentially_affected) {
+    std::ostringstream w;
+    w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0095) << "\n";
+    w << "RPATH entries for target '" << this->Target->GetName() << "' "
+      << "will not be escaped in the intermediary "
+      << "cmake_install.cmake script.";
+    this->Target->GetGlobalGenerator()->GetCMakeInstance()->IssueMessage(
+      MessageType::AUTHOR_WARNING, w.str(), this->GetBacktrace());
+  }
+}
diff --git a/Source/cmInstallTargetGenerator.h b/Source/cmInstallTargetGenerator.h
index ed3ab52..9ccad63 100644
--- a/Source/cmInstallTargetGenerator.h
+++ b/Source/cmInstallTargetGenerator.h
@@ -104,6 +104,7 @@
                      const std::string& toDestDirPath);
   void AddUniversalInstallRule(std::ostream& os, Indent indent,
                                const std::string& toDestDirPath);
+  void IssueCMP0095Warning(const std::string& unescapedRpath);
 
   std::string TargetName;
   cmGeneratorTarget* Target;
diff --git a/Source/cmJsonObjects.cxx b/Source/cmJsonObjects.cxx
index 636a8e1..8d065e1 100644
--- a/Source/cmJsonObjects.cxx
+++ b/Source/cmJsonObjects.cxx
@@ -14,7 +14,6 @@
 #include "cmLinkLineComputer.h"
 #include "cmLocalGenerator.h"
 #include "cmMakefile.h"
-#include "cmProperty.h"
 #include "cmPropertyMap.h"
 #include "cmSourceFile.h"
 #include "cmState.h"
@@ -363,12 +362,12 @@
 
   // Build up the list of properties that may have been specified
   Json::Value properties = Json::arrayValue;
-  for (auto& prop : testInfo->GetProperties()) {
+  for (auto& prop : testInfo->GetProperties().GetList()) {
     Json::Value entry = Json::objectValue;
     entry[kKEY_KEY] = prop.first;
 
     // Remove config variables from the value too.
-    auto cge_value = ge.Parse(prop.second.GetValue());
+    auto cge_value = ge.Parse(prop.second);
     const std::string& processed_value = cge_value->Evaluate(lg, config);
     entry[kVALUE_KEY] = processed_value;
     properties.append(entry);
diff --git a/Source/cmLDConfigLDConfigTool.cxx b/Source/cmLDConfigLDConfigTool.cxx
new file mode 100644
index 0000000..586ea96
--- /dev/null
+++ b/Source/cmLDConfigLDConfigTool.cxx
@@ -0,0 +1,70 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#include "cmLDConfigLDConfigTool.h"
+#include "cmMakefile.h"
+#include "cmRuntimeDependencyArchive.h"
+#include "cmSystemTools.h"
+#include "cmUVProcessChain.h"
+
+#include "cmsys/RegularExpression.hxx"
+
+#include <istream>
+#include <string>
+#include <vector>
+
+cmLDConfigLDConfigTool::cmLDConfigLDConfigTool(
+  cmRuntimeDependencyArchive* archive)
+  : cmLDConfigTool(archive)
+{
+}
+
+bool cmLDConfigLDConfigTool::GetLDConfigPaths(std::vector<std::string>& paths)
+{
+  std::string ldConfigPath =
+    this->Archive->GetMakefile()->GetSafeDefinition("CMAKE_LDCONFIG_COMMAND");
+  if (ldConfigPath.empty()) {
+    ldConfigPath = cmSystemTools::FindProgram(
+      "ldconfig", { "/sbin", "/usr/sbin", "/usr/local/sbin" });
+    if (ldConfigPath.empty()) {
+      this->Archive->SetError("Could not find ldconfig");
+      return false;
+    }
+  }
+
+  std::vector<std::string> ldConfigCommand;
+  cmSystemTools::ExpandListArgument(ldConfigPath, ldConfigCommand);
+  ldConfigCommand.emplace_back("-v");
+  ldConfigCommand.emplace_back("-N"); // Don't rebuild the cache.
+  ldConfigCommand.emplace_back("-X"); // Don't update links.
+
+  cmUVProcessChainBuilder builder;
+  builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT)
+    .AddCommand(ldConfigCommand);
+  auto process = builder.Start();
+  if (!process.Valid()) {
+    this->Archive->SetError("Failed to start ldconfig process");
+    return false;
+  }
+
+  std::string line;
+  static const cmsys::RegularExpression regex("^([^\t:]*):");
+  while (std::getline(*process.OutputStream(), line)) {
+    cmsys::RegularExpressionMatch match;
+    if (regex.find(line.c_str(), match)) {
+      paths.push_back(match.match(1));
+    }
+  }
+
+  if (!process.Wait()) {
+    this->Archive->SetError("Failed to wait on ldconfig process");
+    return false;
+  }
+  auto status = process.GetStatus();
+  if (!status[0] || status[0]->ExitStatus != 0) {
+    this->Archive->SetError("Failed to run ldconfig");
+    return false;
+  }
+
+  return true;
+}
diff --git a/Source/cmLDConfigLDConfigTool.h b/Source/cmLDConfigLDConfigTool.h
new file mode 100644
index 0000000..d945a9b
--- /dev/null
+++ b/Source/cmLDConfigLDConfigTool.h
@@ -0,0 +1,22 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#ifndef cmLDConfigLDConfigTool_h
+#define cmLDConfigLDConfigTool_h
+
+#include "cmLDConfigTool.h"
+
+#include <string>
+#include <vector>
+
+class cmRuntimeDependencyArchive;
+
+class cmLDConfigLDConfigTool : public cmLDConfigTool
+{
+public:
+  cmLDConfigLDConfigTool(cmRuntimeDependencyArchive* archive);
+
+  bool GetLDConfigPaths(std::vector<std::string>& paths) override;
+};
+
+#endif
diff --git a/Source/cmLDConfigTool.cxx b/Source/cmLDConfigTool.cxx
new file mode 100644
index 0000000..8d5d563
--- /dev/null
+++ b/Source/cmLDConfigTool.cxx
@@ -0,0 +1,9 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#include "cmLDConfigTool.h"
+
+cmLDConfigTool::cmLDConfigTool(cmRuntimeDependencyArchive* archive)
+  : Archive(archive)
+{
+}
diff --git a/Source/cmLDConfigTool.h b/Source/cmLDConfigTool.h
new file mode 100644
index 0000000..c816562
--- /dev/null
+++ b/Source/cmLDConfigTool.h
@@ -0,0 +1,24 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#ifndef cmLDConfigTool_h
+#define cmLDConfigTool_h
+
+#include <string>
+#include <vector>
+
+class cmRuntimeDependencyArchive;
+
+class cmLDConfigTool
+{
+public:
+  cmLDConfigTool(cmRuntimeDependencyArchive* archive);
+  virtual ~cmLDConfigTool() = default;
+
+  virtual bool GetLDConfigPaths(std::vector<std::string>& paths) = 0;
+
+protected:
+  cmRuntimeDependencyArchive* Archive;
+};
+
+#endif
diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx
index fe5c8af..3abf2dd 100644
--- a/Source/cmLocalGenerator.cxx
+++ b/Source/cmLocalGenerator.cxx
@@ -1444,10 +1444,23 @@
 
   std::string linkLanguage = cli.GetLinkLanguage();
 
-  const std::string& libPathFlag =
-    this->Makefile->GetRequiredDefinition("CMAKE_LIBRARY_PATH_FLAG");
-  const std::string& libPathTerminator =
-    this->Makefile->GetSafeDefinition("CMAKE_LIBRARY_PATH_TERMINATOR");
+  std::string libPathFlag;
+  if (const char* value = this->Makefile->GetDefinition(
+        "CMAKE_" + cli.GetLinkLanguage() + "_LIBRARY_PATH_FLAG")) {
+    libPathFlag = value;
+  } else {
+    libPathFlag =
+      this->Makefile->GetRequiredDefinition("CMAKE_LIBRARY_PATH_FLAG");
+  }
+
+  std::string libPathTerminator;
+  if (const char* value = this->Makefile->GetDefinition(
+        "CMAKE_" + cli.GetLinkLanguage() + "_LIBRARY_PATH_TERMINATOR")) {
+    libPathTerminator = value;
+  } else {
+    libPathTerminator =
+      this->Makefile->GetRequiredDefinition("CMAKE_LIBRARY_PATH_TERMINATOR");
+  }
 
   // Add standard libraries for this language.
   std::string standardLibsVar = "CMAKE_";
diff --git a/Source/cmOutputConverter.cxx b/Source/cmOutputConverter.cxx
index 7d88b08..d7bcf7e 100644
--- a/Source/cmOutputConverter.cxx
+++ b/Source/cmOutputConverter.cxx
@@ -73,22 +73,10 @@
 
 static bool cmOutputConverterIsShellOperator(const std::string& str)
 {
-  static std::set<std::string> shellOperators;
-  if (shellOperators.empty()) {
-    shellOperators.insert("<");
-    shellOperators.insert(">");
-    shellOperators.insert("<<");
-    shellOperators.insert(">>");
-    shellOperators.insert("|");
-    shellOperators.insert("||");
-    shellOperators.insert("&&");
-    shellOperators.insert("&>");
-    shellOperators.insert("1>");
-    shellOperators.insert("2>");
-    shellOperators.insert("2>&1");
-    shellOperators.insert("1>&2");
-  }
-  return shellOperators.count(str) > 0;
+  static std::set<std::string> const shellOperators{
+    "<", ">", "<<", ">>", "|", "||", "&&", "&>", "1>", "2>", "2>&1", "1>&2"
+  };
+  return (shellOperators.count(str) != 0);
 }
 
 std::string cmOutputConverter::EscapeForShell(const std::string& str,
diff --git a/Source/cmPolicies.h b/Source/cmPolicies.h
index b705119..c16a46f 100644
--- a/Source/cmPolicies.h
+++ b/Source/cmPolicies.h
@@ -279,7 +279,11 @@
   SELECT(POLICY, CMP0094,                                                     \
          "FindPython3,  FindPython2 and FindPyton use "                       \
          "LOCATION for lookup strategy.",                                     \
-         3, 15, 0, cmPolicies::WARN)
+         3, 15, 0, cmPolicies::WARN)                                          \
+  SELECT(POLICY, CMP0095,                                                     \
+         "RPATH entries are properly escaped in the intermediary CMake "      \
+         "install script.",                                                   \
+         3, 16, 0, cmPolicies::WARN)
 
 #define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1)
 #define CM_FOR_EACH_POLICY_ID(POLICY)                                         \
@@ -307,7 +311,8 @@
   F(CMP0073)                                                                  \
   F(CMP0076)                                                                  \
   F(CMP0081)                                                                  \
-  F(CMP0083)
+  F(CMP0083)                                                                  \
+  F(CMP0095)
 
 /** \class cmPolicies
  * \brief Handles changes in CMake behavior and policies
diff --git a/Source/cmProperty.cxx b/Source/cmProperty.cxx
deleted file mode 100644
index 27f0ecd..0000000
--- a/Source/cmProperty.cxx
+++ /dev/null
@@ -1,26 +0,0 @@
-/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
-   file Copyright.txt or https://cmake.org/licensing for details.  */
-#include "cmProperty.h"
-
-void cmProperty::Set(const char* value)
-{
-  this->Value = value;
-  this->ValueHasBeenSet = true;
-}
-
-void cmProperty::Append(const char* value, bool asString)
-{
-  if (!this->Value.empty() && *value && !asString) {
-    this->Value += ";";
-  }
-  this->Value += value;
-  this->ValueHasBeenSet = true;
-}
-
-const char* cmProperty::GetValue() const
-{
-  if (this->ValueHasBeenSet) {
-    return this->Value.c_str();
-  }
-  return nullptr;
-}
diff --git a/Source/cmProperty.h b/Source/cmProperty.h
index d11c5ef..80f131a 100644
--- a/Source/cmProperty.h
+++ b/Source/cmProperty.h
@@ -5,8 +5,6 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include <string>
-
 class cmProperty
 {
 public:
@@ -22,22 +20,6 @@
     CACHED_VARIABLE,
     INSTALL
   };
-
-  // set this property
-  void Set(const char* value);
-
-  // append to this property
-  void Append(const char* value, bool asString = false);
-
-  // get the value
-  const char* GetValue() const;
-
-  // construct with the value not set
-  cmProperty() { this->ValueHasBeenSet = false; }
-
-protected:
-  std::string Value;
-  bool ValueHasBeenSet;
 };
 
 #endif
diff --git a/Source/cmPropertyMap.cxx b/Source/cmPropertyMap.cxx
index 3f6d7c8..3ed4c05 100644
--- a/Source/cmPropertyMap.cxx
+++ b/Source/cmPropertyMap.cxx
@@ -3,40 +3,21 @@
 #include "cmPropertyMap.h"
 
 #include <algorithm>
-#include <assert.h>
 #include <utility>
 
-cmProperty* cmPropertyMap::GetOrCreateProperty(const std::string& name)
+void cmPropertyMap::Clear()
 {
-  cmPropertyMap::iterator it = this->find(name);
-  cmProperty* prop;
-  if (it == this->end()) {
-    prop = &(*this)[name];
-  } else {
-    prop = &(it->second);
-  }
-  return prop;
-}
-
-std::vector<std::string> cmPropertyMap::GetPropertyList() const
-{
-  std::vector<std::string> keyList;
-  for (auto const& i : *this) {
-    keyList.push_back(i.first);
-  }
-  std::sort(keyList.begin(), keyList.end());
-  return keyList;
+  Map_.clear();
 }
 
 void cmPropertyMap::SetProperty(const std::string& name, const char* value)
 {
   if (!value) {
-    this->erase(name);
+    Map_.erase(name);
     return;
   }
 
-  cmProperty* prop = this->GetOrCreateProperty(name);
-  prop->Set(value);
+  Map_[name] = value;
 }
 
 void cmPropertyMap::AppendProperty(const std::string& name, const char* value,
@@ -47,17 +28,53 @@
     return;
   }
 
-  cmProperty* prop = this->GetOrCreateProperty(name);
-  prop->Append(value, asString);
+  {
+    std::string& pVal = Map_[name];
+    if (!pVal.empty() && !asString) {
+      pVal += ';';
+    }
+    pVal += value;
+  }
+}
+
+void cmPropertyMap::RemoveProperty(const std::string& name)
+{
+  Map_.erase(name);
 }
 
 const char* cmPropertyMap::GetPropertyValue(const std::string& name) const
 {
-  assert(!name.empty());
-
-  cmPropertyMap::const_iterator it = this->find(name);
-  if (it == this->end()) {
-    return nullptr;
+  {
+    auto it = Map_.find(name);
+    if (it != Map_.end()) {
+      return it->second.c_str();
+    }
   }
-  return it->second.GetValue();
+  return nullptr;
+}
+
+std::vector<std::string> cmPropertyMap::GetKeys() const
+{
+  std::vector<std::string> keyList;
+  keyList.reserve(Map_.size());
+  for (auto const& item : Map_) {
+    keyList.push_back(item.first);
+  }
+  std::sort(keyList.begin(), keyList.end());
+  return keyList;
+}
+
+std::vector<std::pair<std::string, std::string>> cmPropertyMap::GetList() const
+{
+  typedef std::pair<std::string, std::string> StringPair;
+  std::vector<StringPair> kvList;
+  kvList.reserve(Map_.size());
+  for (auto const& item : Map_) {
+    kvList.emplace_back(item.first, item.second);
+  }
+  std::sort(kvList.begin(), kvList.end(),
+            [](StringPair const& a, StringPair const& b) {
+              return a.first < b.first;
+            });
+  return kvList;
 }
diff --git a/Source/cmPropertyMap.h b/Source/cmPropertyMap.h
index 5a05150..9aed349 100644
--- a/Source/cmPropertyMap.h
+++ b/Source/cmPropertyMap.h
@@ -5,25 +5,47 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmProperty.h"
-
-#include <map>
 #include <string>
+#include <unordered_map>
+#include <utility>
 #include <vector>
 
-class cmPropertyMap : public std::map<std::string, cmProperty>
+/** \class cmPropertyMap
+ * \brief String property map.
+ */
+class cmPropertyMap
 {
 public:
-  cmProperty* GetOrCreateProperty(const std::string& name);
+  // -- General
 
-  std::vector<std::string> GetPropertyList() const;
+  //! Clear property list
+  void Clear();
 
+  // -- Properties
+
+  //! Set the property value
   void SetProperty(const std::string& name, const char* value);
 
+  //! Append to the property value
   void AppendProperty(const std::string& name, const char* value,
                       bool asString = false);
 
+  //! Get the property value
   const char* GetPropertyValue(const std::string& name) const;
+
+  //! Remove the property @a name from the map
+  void RemoveProperty(const std::string& name);
+
+  // -- Lists
+
+  //! Get a sorted list of property keys
+  std::vector<std::string> GetKeys() const;
+
+  //! Get a sorted by key list of property key,value pairs
+  std::vector<std::pair<std::string, std::string>> GetList() const;
+
+private:
+  std::unordered_map<std::string, std::string> Map_;
 };
 
 #endif
diff --git a/Source/cmRuntimeDependencyArchive.cxx b/Source/cmRuntimeDependencyArchive.cxx
new file mode 100644
index 0000000..b4c6c32
--- /dev/null
+++ b/Source/cmRuntimeDependencyArchive.cxx
@@ -0,0 +1,378 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#include "cmRuntimeDependencyArchive.h"
+
+#include "cmAlgorithms.h"
+#include "cmBinUtilsLinuxELFLinker.h"
+#include "cmBinUtilsMacOSMachOLinker.h"
+#include "cmBinUtilsWindowsPELinker.h"
+#include "cmCommand.h"
+#include "cmMakefile.h"
+#include "cmStateTypes.h"
+#include "cmSystemTools.h"
+
+#if defined(_WIN32)
+#  include "cmGlobalGenerator.h"
+#  ifdef CMAKE_BUILD_WITH_CMAKE
+#    include "cmGlobalVisualStudioVersionedGenerator.h"
+#  endif
+#  include "cmVSSetupHelper.h"
+#  include "cmsys/Glob.hxx"
+#endif
+
+#include <algorithm>
+#include <sstream>
+#include <string>
+#include <utility>
+#include <vector>
+
+#if defined(_WIN32)
+static void AddVisualStudioPath(std::vector<std::string>& paths,
+                                const std::string& prefix,
+                                unsigned int version, cmGlobalGenerator* gg)
+{
+  // If generating for the VS IDE, use the same instance.
+  std::string vsloc;
+  bool found = false;
+#  ifdef CMAKE_BUILD_WITH_CMAKE
+  if (gg->GetName().find(prefix) == 0) {
+    cmGlobalVisualStudioVersionedGenerator* vsgen =
+      static_cast<cmGlobalVisualStudioVersionedGenerator*>(gg);
+    if (vsgen->GetVSInstance(vsloc)) {
+      found = true;
+    }
+  }
+#  endif
+
+  // Otherwise, find a VS instance ourselves.
+  if (!found) {
+    cmVSSetupAPIHelper vsSetupAPIHelper(version);
+    if (vsSetupAPIHelper.GetVSInstanceInfo(vsloc)) {
+      cmSystemTools::ConvertToUnixSlashes(vsloc);
+      found = true;
+    }
+  }
+
+  if (found) {
+    cmsys::Glob glob;
+    glob.SetListDirs(true);
+    glob.FindFiles(vsloc + "/VC/Tools/MSVC/*");
+    for (auto const& vcdir : glob.GetFiles()) {
+      paths.push_back(vcdir + "/bin/Hostx64/x64");
+      paths.push_back(vcdir + "/bin/Hostx86/x64");
+      paths.push_back(vcdir + "/bin/Hostx64/x86");
+      paths.push_back(vcdir + "/bin/Hostx86/x86");
+    }
+  }
+}
+
+static void AddRegistryPath(std::vector<std::string>& paths,
+                            const std::string& path, cmMakefile* mf)
+{
+  // We should view the registry as the target application would view
+  // it.
+  cmSystemTools::KeyWOW64 view = cmSystemTools::KeyWOW64_32;
+  cmSystemTools::KeyWOW64 other_view = cmSystemTools::KeyWOW64_64;
+  if (mf->PlatformIs64Bit()) {
+    view = cmSystemTools::KeyWOW64_64;
+    other_view = cmSystemTools::KeyWOW64_32;
+  }
+
+  // Expand using the view of the target application.
+  std::string expanded = path;
+  cmSystemTools::ExpandRegistryValues(expanded, view);
+  cmSystemTools::GlobDirs(expanded, paths);
+
+  // Executables can be either 32-bit or 64-bit, so expand using the
+  // alternative view.
+  expanded = path;
+  cmSystemTools::ExpandRegistryValues(expanded, other_view);
+  cmSystemTools::GlobDirs(expanded, paths);
+}
+
+static void AddEnvPath(std::vector<std::string>& paths, const std::string& var,
+                       const std::string& suffix)
+{
+  std::string value;
+  if (cmSystemTools::GetEnv(var, value)) {
+    paths.push_back(value + suffix);
+  }
+}
+#endif
+
+static cmsys::RegularExpression TransformCompile(const std::string& str)
+{
+  return cmsys::RegularExpression(str);
+}
+
+cmRuntimeDependencyArchive::cmRuntimeDependencyArchive(
+  cmCommand* command, std::vector<std::string> searchDirectories,
+  std::string bundleExecutable,
+  const std::vector<std::string>& preIncludeRegexes,
+  const std::vector<std::string>& preExcludeRegexes,
+  const std::vector<std::string>& postIncludeRegexes,
+  const std::vector<std::string>& postExcludeRegexes)
+  : Command(command)
+  , SearchDirectories(std::move(searchDirectories))
+  , BundleExecutable(std::move(bundleExecutable))
+  , PreIncludeRegexes(preIncludeRegexes.size())
+  , PreExcludeRegexes(preExcludeRegexes.size())
+  , PostIncludeRegexes(postIncludeRegexes.size())
+  , PostExcludeRegexes(postExcludeRegexes.size())
+{
+  std::transform(preIncludeRegexes.begin(), preIncludeRegexes.end(),
+                 this->PreIncludeRegexes.begin(), TransformCompile);
+  std::transform(preExcludeRegexes.begin(), preExcludeRegexes.end(),
+                 this->PreExcludeRegexes.begin(), TransformCompile);
+  std::transform(postIncludeRegexes.begin(), postIncludeRegexes.end(),
+                 this->PostIncludeRegexes.begin(), TransformCompile);
+  std::transform(postExcludeRegexes.begin(), postExcludeRegexes.end(),
+                 this->PostExcludeRegexes.begin(), TransformCompile);
+}
+
+bool cmRuntimeDependencyArchive::Prepare()
+{
+  std::string platform = this->GetMakefile()->GetSafeDefinition(
+    "CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM");
+  if (platform.empty()) {
+    std::string systemName =
+      this->GetMakefile()->GetSafeDefinition("CMAKE_HOST_SYSTEM_NAME");
+    if (systemName == "Windows") {
+      platform = "windows+pe";
+    } else if (systemName == "Darwin") {
+      platform = "macos+macho";
+    } else if (systemName == "Linux") {
+      platform = "linux+elf";
+    }
+  }
+  if (platform == "linux+elf") {
+    this->Linker = cm::make_unique<cmBinUtilsLinuxELFLinker>(this);
+  } else if (platform == "windows+pe") {
+    this->Linker = cm::make_unique<cmBinUtilsWindowsPELinker>(this);
+  } else if (platform == "macos+macho") {
+    this->Linker = cm::make_unique<cmBinUtilsMacOSMachOLinker>(this);
+  } else {
+    std::ostringstream e;
+    e << "Invalid value for CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM: "
+      << platform;
+    this->SetError(e.str());
+    return false;
+  }
+
+  return this->Linker->Prepare();
+}
+
+bool cmRuntimeDependencyArchive::GetRuntimeDependencies(
+  const std::vector<std::string>& executables,
+  const std::vector<std::string>& libraries,
+  const std::vector<std::string>& modules)
+{
+  for (auto const& exe : executables) {
+    if (!this->Linker->ScanDependencies(exe, cmStateEnums::EXECUTABLE)) {
+      return false;
+    }
+  }
+  for (auto const& lib : libraries) {
+    if (!this->Linker->ScanDependencies(lib, cmStateEnums::SHARED_LIBRARY)) {
+      return false;
+    }
+  }
+  for (auto const& mod : modules) {
+    if (!this->Linker->ScanDependencies(mod, cmStateEnums::MODULE_LIBRARY)) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+void cmRuntimeDependencyArchive::SetError(const std::string& e)
+{
+  this->Command->SetError(e);
+}
+
+std::string cmRuntimeDependencyArchive::GetBundleExecutable()
+{
+  return this->BundleExecutable;
+}
+
+const std::vector<std::string>&
+cmRuntimeDependencyArchive::GetSearchDirectories()
+{
+  return this->SearchDirectories;
+}
+
+std::string cmRuntimeDependencyArchive::GetGetRuntimeDependenciesTool()
+{
+  return this->GetMakefile()->GetSafeDefinition(
+    "CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL");
+}
+
+bool cmRuntimeDependencyArchive::GetGetRuntimeDependenciesCommand(
+  const std::string& search, std::vector<std::string>& command)
+{
+  // First see if it was supplied by the user
+  std::string toolCommand = this->GetMakefile()->GetSafeDefinition(
+    "CMAKE_GET_RUNTIME_DEPENDENCIES_COMMAND");
+  if (!toolCommand.empty()) {
+    cmSystemTools::ExpandListArgument(toolCommand, command);
+    return true;
+  }
+
+  // Now go searching for it
+  std::vector<std::string> paths;
+#ifdef _WIN32
+  cmGlobalGenerator* gg = this->GetMakefile()->GetGlobalGenerator();
+
+  // Add newer Visual Studio paths
+  AddVisualStudioPath(paths, "Visual Studio 16 ", 16, gg);
+  AddVisualStudioPath(paths, "Visual Studio 15 ", 15, gg);
+
+  // Add older Visual Studio paths
+  AddRegistryPath(
+    paths,
+    "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\14.0;InstallDir]/"
+    "../../VC/bin",
+    this->GetMakefile());
+  AddEnvPath(paths, "VS140COMNTOOLS", "/../../VC/bin");
+  paths.push_back(
+    "C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin");
+  AddRegistryPath(
+    paths,
+    "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\12.0;InstallDir]/"
+    "../../VC/bin",
+    this->GetMakefile());
+  AddEnvPath(paths, "VS120COMNTOOLS", "/../../VC/bin");
+  paths.push_back(
+    "C:/Program Files (x86)/Microsoft Visual Studio 12.0/VC/bin");
+  AddRegistryPath(
+    paths,
+    "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\11.0;InstallDir]/"
+    "../../VC/bin",
+    this->GetMakefile());
+  AddEnvPath(paths, "VS110COMNTOOLS", "/../../VC/bin");
+  paths.push_back(
+    "C:/Program Files (x86)/Microsoft Visual Studio 11.0/VC/bin");
+  AddRegistryPath(
+    paths,
+    "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\10.0;InstallDir]/"
+    "../../VC/bin",
+    this->GetMakefile());
+  AddEnvPath(paths, "VS100COMNTOOLS", "/../../VC/bin");
+  paths.push_back(
+    "C:/Program Files (x86)/Microsoft Visual Studio 10.0/VC/bin");
+  AddRegistryPath(
+    paths,
+    "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\9.0;InstallDir]/"
+    "../../VC/bin",
+    this->GetMakefile());
+  AddEnvPath(paths, "VS90COMNTOOLS", "/../../VC/bin");
+  paths.push_back("C:/Program Files/Microsoft Visual Studio 9.0/VC/bin");
+  paths.push_back("C:/Program Files (x86)/Microsoft Visual Studio 9.0/VC/bin");
+  AddRegistryPath(
+    paths,
+    "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\8.0;InstallDir]/"
+    "../../VC/bin",
+    this->GetMakefile());
+  AddEnvPath(paths, "VS80COMNTOOLS", "/../../VC/bin");
+  paths.push_back("C:/Program Files/Microsoft Visual Studio 8/VC/BIN");
+  paths.push_back("C:/Program Files (x86)/Microsoft Visual Studio 8/VC/BIN");
+  AddRegistryPath(
+    paths,
+    "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\7.1;InstallDir]/"
+    "../../VC7/bin",
+    this->GetMakefile());
+  AddEnvPath(paths, "VS71COMNTOOLS", "/../../VC7/bin");
+  paths.push_back(
+    "C:/Program Files/Microsoft Visual Studio .NET 2003/VC7/BIN");
+  paths.push_back(
+    "C:/Program Files (x86)/Microsoft Visual Studio .NET 2003/VC7/BIN");
+#endif
+
+  std::string program = cmSystemTools::FindProgram(search, paths);
+  if (!program.empty()) {
+    command = { program };
+    return true;
+  }
+
+  // Couldn't find it
+  return false;
+}
+
+bool cmRuntimeDependencyArchive::IsPreExcluded(const std::string& name)
+{
+  cmsys::RegularExpressionMatch match;
+
+  for (auto const& regex : this->PreIncludeRegexes) {
+    if (regex.find(name.c_str(), match)) {
+      return false;
+    }
+  }
+
+  for (auto const& regex : this->PreExcludeRegexes) {
+    if (regex.find(name.c_str(), match)) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+bool cmRuntimeDependencyArchive::IsPostExcluded(const std::string& name)
+{
+  cmsys::RegularExpressionMatch match;
+
+  for (auto const& regex : this->PostIncludeRegexes) {
+    if (regex.find(name.c_str(), match)) {
+      return false;
+    }
+  }
+
+  for (auto const& regex : this->PostExcludeRegexes) {
+    if (regex.find(name.c_str(), match)) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+void cmRuntimeDependencyArchive::AddResolvedPath(const std::string& name,
+                                                 const std::string& path,
+                                                 bool& unique)
+{
+  auto it =
+    this->ResolvedPaths
+      .insert(std::pair<std::string, std::set<std::string>>{ name, {} })
+      .first;
+  unique = true;
+  for (auto const& other : it->second) {
+    if (cmSystemTools::SameFile(path, other)) {
+      unique = false;
+      break;
+    }
+  }
+  it->second.insert(path);
+}
+
+void cmRuntimeDependencyArchive::AddUnresolvedPath(const std::string& name)
+{
+  this->UnresolvedPaths.insert(name);
+}
+
+cmMakefile* cmRuntimeDependencyArchive::GetMakefile()
+{
+  return this->Command->GetMakefile();
+}
+
+const std::map<std::string, std::set<std::string>>&
+cmRuntimeDependencyArchive::GetResolvedPaths()
+{
+  return this->ResolvedPaths;
+}
+
+const std::set<std::string>& cmRuntimeDependencyArchive::GetUnresolvedPaths()
+{
+  return this->UnresolvedPaths;
+}
diff --git a/Source/cmRuntimeDependencyArchive.h b/Source/cmRuntimeDependencyArchive.h
new file mode 100644
index 0000000..ec3ecd4
--- /dev/null
+++ b/Source/cmRuntimeDependencyArchive.h
@@ -0,0 +1,70 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#ifndef cmRuntimeDependencyArchive_h
+#define cmRuntimeDependencyArchive_h
+
+#include "cmBinUtilsLinker.h"
+
+#include "cmsys/RegularExpression.hxx"
+
+#include <map>
+#include <memory> // IWYU pragma: keep
+#include <set>
+#include <string>
+#include <vector>
+
+class cmCommand;
+class cmMakefile;
+
+class cmRuntimeDependencyArchive
+{
+public:
+  explicit cmRuntimeDependencyArchive(
+    cmCommand* command, std::vector<std::string> searchDirectories,
+    std::string bundleExecutable,
+    const std::vector<std::string>& preIncludeRegexes,
+    const std::vector<std::string>& preExcludeRegexes,
+    const std::vector<std::string>& postIncludeRegexes,
+    const std::vector<std::string>& postExcludeRegexes);
+  bool Prepare();
+  bool GetRuntimeDependencies(const std::vector<std::string>& executables,
+                              const std::vector<std::string>& libraries,
+                              const std::vector<std::string>& modules);
+
+  void SetError(const std::string& e);
+
+  std::string GetBundleExecutable();
+  const std::vector<std::string>& GetSearchDirectories();
+  std::string GetGetRuntimeDependenciesTool();
+  bool GetGetRuntimeDependenciesCommand(const std::string& search,
+                                        std::vector<std::string>& command);
+  bool IsPreExcluded(const std::string& name);
+  bool IsPostExcluded(const std::string& name);
+
+  void AddResolvedPath(const std::string& name, const std::string& path,
+                       bool& unique);
+  void AddUnresolvedPath(const std::string& name);
+
+  cmMakefile* GetMakefile();
+  const std::map<std::string, std::set<std::string>>& GetResolvedPaths();
+  const std::set<std::string>& GetUnresolvedPaths();
+
+private:
+  cmCommand* Command;
+  std::unique_ptr<cmBinUtilsLinker> Linker;
+
+  std::string GetRuntimeDependenciesTool;
+  std::vector<std::string> GetRuntimeDependenciesCommand;
+
+  std::vector<std::string> SearchDirectories;
+  std::string BundleExecutable;
+  std::vector<cmsys::RegularExpression> PreIncludeRegexes;
+  std::vector<cmsys::RegularExpression> PreExcludeRegexes;
+  std::vector<cmsys::RegularExpression> PostIncludeRegexes;
+  std::vector<cmsys::RegularExpression> PostExcludeRegexes;
+  std::map<std::string, std::set<std::string>> ResolvedPaths;
+  std::set<std::string> UnresolvedPaths;
+};
+
+#endif // cmRuntimeDependencyArchive_h
diff --git a/Source/cmState.cxx b/Source/cmState.cxx
index fa7df0b..091c2e0 100644
--- a/Source/cmState.cxx
+++ b/Source/cmState.cxx
@@ -267,7 +267,7 @@
 
 cmStateSnapshot cmState::Reset()
 {
-  this->GlobalProperties.clear();
+  this->GlobalProperties.Clear();
   this->PropertyDefinitions.clear();
   this->GlobVerificationManager->Reset();
 
@@ -289,7 +289,7 @@
     it->LinkDirectoriesBacktraces.clear();
     it->DirectoryEnd = pos;
     it->NormalTargetNames.clear();
-    it->Properties.clear();
+    it->Properties.Clear();
     it->Children.clear();
   }
 
diff --git a/Source/cmStateDirectory.cxx b/Source/cmStateDirectory.cxx
index 182d3fe..6ca1c9f 100644
--- a/Source/cmStateDirectory.cxx
+++ b/Source/cmStateDirectory.cxx
@@ -6,7 +6,6 @@
 #include <algorithm>
 #include <assert.h>
 #include <iterator>
-#include <utility>
 
 #include "cmAlgorithms.h"
 #include "cmProperty.h"
@@ -667,12 +666,7 @@
 
 std::vector<std::string> cmStateDirectory::GetPropertyKeys() const
 {
-  std::vector<std::string> keys;
-  keys.reserve(this->DirectoryState->Properties.size());
-  for (auto const& it : this->DirectoryState->Properties) {
-    keys.push_back(it.first);
-  }
-  return keys;
+  return this->DirectoryState->Properties.GetKeys();
 }
 
 void cmStateDirectory::AddNormalTargetName(std::string const& name)
diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx
index cd67586..b10b30f 100644
--- a/Source/cmTarget.cxx
+++ b/Source/cmTarget.cxx
@@ -231,14 +231,21 @@
   impl->IsAndroid =
     (impl->Makefile->GetSafeDefinition("CMAKE_SYSTEM_NAME") == "Android");
 
-  std::string gKey;
-  gKey.reserve(128);
-  gKey += "CMAKE_";
-  auto InitProperty = [this, mf, &gKey](const std::string& property,
-                                        const char* default_value) {
+  std::string defKey;
+  defKey.reserve(128);
+  defKey += "CMAKE_";
+  auto initProp = [this, mf, &defKey](const std::string& property) {
     // Replace everything after "CMAKE_"
-    gKey.replace(gKey.begin() + 6, gKey.end(), property);
-    if (const char* value = mf->GetDefinition(gKey)) {
+    defKey.replace(defKey.begin() + 6, defKey.end(), property);
+    if (const char* value = mf->GetDefinition(defKey)) {
+      this->SetProperty(property, value);
+    }
+  };
+  auto initPropValue = [this, mf, &defKey](const std::string& property,
+                                           const char* default_value) {
+    // Replace everything after "CMAKE_"
+    defKey.replace(defKey.begin() + 6, defKey.end(), property);
+    if (const char* value = mf->GetDefinition(defKey)) {
       this->SetProperty(property, value);
     } else if (default_value) {
       this->SetProperty(property, default_value);
@@ -248,107 +255,107 @@
   // Setup default property values.
   if (this->GetType() != cmStateEnums::INTERFACE_LIBRARY &&
       this->GetType() != cmStateEnums::UTILITY) {
-    InitProperty("ANDROID_API", nullptr);
-    InitProperty("ANDROID_API_MIN", nullptr);
-    InitProperty("ANDROID_ARCH", nullptr);
-    InitProperty("ANDROID_STL_TYPE", nullptr);
-    InitProperty("ANDROID_SKIP_ANT_STEP", nullptr);
-    InitProperty("ANDROID_PROCESS_MAX", nullptr);
-    InitProperty("ANDROID_PROGUARD", nullptr);
-    InitProperty("ANDROID_PROGUARD_CONFIG_PATH", nullptr);
-    InitProperty("ANDROID_SECURE_PROPS_PATH", nullptr);
-    InitProperty("ANDROID_NATIVE_LIB_DIRECTORIES", nullptr);
-    InitProperty("ANDROID_NATIVE_LIB_DEPENDENCIES", nullptr);
-    InitProperty("ANDROID_JAVA_SOURCE_DIR", nullptr);
-    InitProperty("ANDROID_JAR_DIRECTORIES", nullptr);
-    InitProperty("ANDROID_JAR_DEPENDENCIES", nullptr);
-    InitProperty("ANDROID_ASSETS_DIRECTORIES", nullptr);
-    InitProperty("ANDROID_ANT_ADDITIONAL_OPTIONS", nullptr);
-    InitProperty("BUILD_RPATH", nullptr);
-    InitProperty("BUILD_RPATH_USE_ORIGIN", nullptr);
-    InitProperty("INSTALL_NAME_DIR", nullptr);
-    InitProperty("INSTALL_RPATH", "");
-    InitProperty("INSTALL_RPATH_USE_LINK_PATH", "OFF");
-    InitProperty("INTERPROCEDURAL_OPTIMIZATION", nullptr);
-    InitProperty("SKIP_BUILD_RPATH", "OFF");
-    InitProperty("BUILD_WITH_INSTALL_RPATH", "OFF");
-    InitProperty("ARCHIVE_OUTPUT_DIRECTORY", nullptr);
-    InitProperty("LIBRARY_OUTPUT_DIRECTORY", nullptr);
-    InitProperty("RUNTIME_OUTPUT_DIRECTORY", nullptr);
-    InitProperty("PDB_OUTPUT_DIRECTORY", nullptr);
-    InitProperty("COMPILE_PDB_OUTPUT_DIRECTORY", nullptr);
-    InitProperty("FRAMEWORK", nullptr);
-    InitProperty("Fortran_FORMAT", nullptr);
-    InitProperty("Fortran_MODULE_DIRECTORY", nullptr);
-    InitProperty("Fortran_COMPILER_LAUNCHER", nullptr);
-    InitProperty("GNUtoMS", nullptr);
-    InitProperty("OSX_ARCHITECTURES", nullptr);
-    InitProperty("IOS_INSTALL_COMBINED", nullptr);
-    InitProperty("AUTOMOC", nullptr);
-    InitProperty("AUTOUIC", nullptr);
-    InitProperty("AUTORCC", nullptr);
-    InitProperty("AUTOGEN_ORIGIN_DEPENDS", nullptr);
-    InitProperty("AUTOGEN_PARALLEL", nullptr);
-    InitProperty("AUTOMOC_COMPILER_PREDEFINES", nullptr);
-    InitProperty("AUTOMOC_DEPEND_FILTERS", nullptr);
-    InitProperty("AUTOMOC_MACRO_NAMES", nullptr);
-    InitProperty("AUTOMOC_MOC_OPTIONS", nullptr);
-    InitProperty("AUTOUIC_OPTIONS", nullptr);
-    InitProperty("AUTOUIC_SEARCH_PATHS", nullptr);
-    InitProperty("AUTORCC_OPTIONS", nullptr);
-    InitProperty("LINK_DEPENDS_NO_SHARED", nullptr);
-    InitProperty("LINK_INTERFACE_LIBRARIES", nullptr);
-    InitProperty("MSVC_RUNTIME_LIBRARY", nullptr);
-    InitProperty("WIN32_EXECUTABLE", nullptr);
-    InitProperty("MACOSX_BUNDLE", nullptr);
-    InitProperty("MACOSX_RPATH", nullptr);
-    InitProperty("NO_SYSTEM_FROM_IMPORTED", nullptr);
-    InitProperty("BUILD_WITH_INSTALL_NAME_DIR", nullptr);
-    InitProperty("C_CLANG_TIDY", nullptr);
-    InitProperty("C_COMPILER_LAUNCHER", nullptr);
-    InitProperty("C_CPPLINT", nullptr);
-    InitProperty("C_CPPCHECK", nullptr);
-    InitProperty("C_INCLUDE_WHAT_YOU_USE", nullptr);
-    InitProperty("LINK_WHAT_YOU_USE", nullptr);
-    InitProperty("C_STANDARD", nullptr);
-    InitProperty("C_STANDARD_REQUIRED", nullptr);
-    InitProperty("C_EXTENSIONS", nullptr);
-    InitProperty("CXX_CLANG_TIDY", nullptr);
-    InitProperty("CXX_COMPILER_LAUNCHER", nullptr);
-    InitProperty("CXX_CPPLINT", nullptr);
-    InitProperty("CXX_CPPCHECK", nullptr);
-    InitProperty("CXX_INCLUDE_WHAT_YOU_USE", nullptr);
-    InitProperty("CXX_STANDARD", nullptr);
-    InitProperty("CXX_STANDARD_REQUIRED", nullptr);
-    InitProperty("CXX_EXTENSIONS", nullptr);
-    InitProperty("CUDA_STANDARD", nullptr);
-    InitProperty("CUDA_STANDARD_REQUIRED", nullptr);
-    InitProperty("CUDA_EXTENSIONS", nullptr);
-    InitProperty("CUDA_COMPILER_LAUNCHER", nullptr);
-    InitProperty("CUDA_SEPARABLE_COMPILATION", nullptr);
-    InitProperty("LINK_SEARCH_START_STATIC", nullptr);
-    InitProperty("LINK_SEARCH_END_STATIC", nullptr);
-    InitProperty("FOLDER", nullptr);
-    InitProperty("Swift_MODULE_DIRECTORY", nullptr);
-    InitProperty("VS_JUST_MY_CODE_DEBUGGING", nullptr);
+    initProp("ANDROID_API");
+    initProp("ANDROID_API_MIN");
+    initProp("ANDROID_ARCH");
+    initProp("ANDROID_STL_TYPE");
+    initProp("ANDROID_SKIP_ANT_STEP");
+    initProp("ANDROID_PROCESS_MAX");
+    initProp("ANDROID_PROGUARD");
+    initProp("ANDROID_PROGUARD_CONFIG_PATH");
+    initProp("ANDROID_SECURE_PROPS_PATH");
+    initProp("ANDROID_NATIVE_LIB_DIRECTORIES");
+    initProp("ANDROID_NATIVE_LIB_DEPENDENCIES");
+    initProp("ANDROID_JAVA_SOURCE_DIR");
+    initProp("ANDROID_JAR_DIRECTORIES");
+    initProp("ANDROID_JAR_DEPENDENCIES");
+    initProp("ANDROID_ASSETS_DIRECTORIES");
+    initProp("ANDROID_ANT_ADDITIONAL_OPTIONS");
+    initProp("BUILD_RPATH");
+    initProp("BUILD_RPATH_USE_ORIGIN");
+    initProp("INSTALL_NAME_DIR");
+    initPropValue("INSTALL_RPATH", "");
+    initPropValue("INSTALL_RPATH_USE_LINK_PATH", "OFF");
+    initProp("INTERPROCEDURAL_OPTIMIZATION");
+    initPropValue("SKIP_BUILD_RPATH", "OFF");
+    initPropValue("BUILD_WITH_INSTALL_RPATH", "OFF");
+    initProp("ARCHIVE_OUTPUT_DIRECTORY");
+    initProp("LIBRARY_OUTPUT_DIRECTORY");
+    initProp("RUNTIME_OUTPUT_DIRECTORY");
+    initProp("PDB_OUTPUT_DIRECTORY");
+    initProp("COMPILE_PDB_OUTPUT_DIRECTORY");
+    initProp("FRAMEWORK");
+    initProp("Fortran_FORMAT");
+    initProp("Fortran_MODULE_DIRECTORY");
+    initProp("Fortran_COMPILER_LAUNCHER");
+    initProp("GNUtoMS");
+    initProp("OSX_ARCHITECTURES");
+    initProp("IOS_INSTALL_COMBINED");
+    initProp("AUTOMOC");
+    initProp("AUTOUIC");
+    initProp("AUTORCC");
+    initProp("AUTOGEN_ORIGIN_DEPENDS");
+    initProp("AUTOGEN_PARALLEL");
+    initProp("AUTOMOC_COMPILER_PREDEFINES");
+    initProp("AUTOMOC_DEPEND_FILTERS");
+    initProp("AUTOMOC_MACRO_NAMES");
+    initProp("AUTOMOC_MOC_OPTIONS");
+    initProp("AUTOUIC_OPTIONS");
+    initProp("AUTOUIC_SEARCH_PATHS");
+    initProp("AUTORCC_OPTIONS");
+    initProp("LINK_DEPENDS_NO_SHARED");
+    initProp("LINK_INTERFACE_LIBRARIES");
+    initProp("MSVC_RUNTIME_LIBRARY");
+    initProp("WIN32_EXECUTABLE");
+    initProp("MACOSX_BUNDLE");
+    initProp("MACOSX_RPATH");
+    initProp("NO_SYSTEM_FROM_IMPORTED");
+    initProp("BUILD_WITH_INSTALL_NAME_DIR");
+    initProp("C_CLANG_TIDY");
+    initProp("C_COMPILER_LAUNCHER");
+    initProp("C_CPPLINT");
+    initProp("C_CPPCHECK");
+    initProp("C_INCLUDE_WHAT_YOU_USE");
+    initProp("LINK_WHAT_YOU_USE");
+    initProp("C_STANDARD");
+    initProp("C_STANDARD_REQUIRED");
+    initProp("C_EXTENSIONS");
+    initProp("CXX_CLANG_TIDY");
+    initProp("CXX_COMPILER_LAUNCHER");
+    initProp("CXX_CPPLINT");
+    initProp("CXX_CPPCHECK");
+    initProp("CXX_INCLUDE_WHAT_YOU_USE");
+    initProp("CXX_STANDARD");
+    initProp("CXX_STANDARD_REQUIRED");
+    initProp("CXX_EXTENSIONS");
+    initProp("CUDA_STANDARD");
+    initProp("CUDA_STANDARD_REQUIRED");
+    initProp("CUDA_EXTENSIONS");
+    initProp("CUDA_COMPILER_LAUNCHER");
+    initProp("CUDA_SEPARABLE_COMPILATION");
+    initProp("LINK_SEARCH_START_STATIC");
+    initProp("LINK_SEARCH_END_STATIC");
+    initProp("FOLDER");
+    initProp("Swift_MODULE_DIRECTORY");
+    initProp("VS_JUST_MY_CODE_DEBUGGING");
 #ifdef __APPLE__
     if (this->GetGlobalGenerator()->IsXcode()) {
-      InitProperty("XCODE_GENERATE_SCHEME", nullptr);
-      InitProperty("XCODE_SCHEME_ADDRESS_SANITIZER", nullptr);
-      InitProperty("XCODE_SCHEME_ADDRESS_SANITIZER_USE_AFTER_RETURN", nullptr);
-      InitProperty("XCODE_SCHEME_THREAD_SANITIZER", nullptr);
-      InitProperty("XCODE_SCHEME_THREAD_SANITIZER_STOP", nullptr);
-      InitProperty("XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER", nullptr);
-      InitProperty("XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER_STOP", nullptr);
-      InitProperty("XCODE_SCHEME_DISABLE_MAIN_THREAD_CHECKER", nullptr);
-      InitProperty("XCODE_SCHEME_MAIN_THREAD_CHECKER_STOP", nullptr);
-      InitProperty("XCODE_SCHEME_MALLOC_SCRIBBLE", nullptr);
-      InitProperty("XCODE_SCHEME_MALLOC_GUARD_EDGES", nullptr);
-      InitProperty("XCODE_SCHEME_GUARD_MALLOC", nullptr);
-      InitProperty("XCODE_SCHEME_ZOMBIE_OBJECTS", nullptr);
-      InitProperty("XCODE_SCHEME_MALLOC_STACK", nullptr);
-      InitProperty("XCODE_SCHEME_DYNAMIC_LINKER_API_USAGE", nullptr);
-      InitProperty("XCODE_SCHEME_DYNAMIC_LIBRARY_LOADS", nullptr);
+      initProp("XCODE_GENERATE_SCHEME");
+      initProp("XCODE_SCHEME_ADDRESS_SANITIZER");
+      initProp("XCODE_SCHEME_ADDRESS_SANITIZER_USE_AFTER_RETURN");
+      initProp("XCODE_SCHEME_THREAD_SANITIZER");
+      initProp("XCODE_SCHEME_THREAD_SANITIZER_STOP");
+      initProp("XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER");
+      initProp("XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER_STOP");
+      initProp("XCODE_SCHEME_DISABLE_MAIN_THREAD_CHECKER");
+      initProp("XCODE_SCHEME_MAIN_THREAD_CHECKER_STOP");
+      initProp("XCODE_SCHEME_MALLOC_SCRIBBLE");
+      initProp("XCODE_SCHEME_MALLOC_GUARD_EDGES");
+      initProp("XCODE_SCHEME_GUARD_MALLOC");
+      initProp("XCODE_SCHEME_ZOMBIE_OBJECTS");
+      initProp("XCODE_SCHEME_MALLOC_STACK");
+      initProp("XCODE_SCHEME_DYNAMIC_LINKER_API_USAGE");
+      initProp("XCODE_SCHEME_DYNAMIC_LIBRARY_LOADS");
     }
 #endif
   }
@@ -376,7 +383,7 @@
         }
         std::string property = prop;
         property += configUpper;
-        InitProperty(property, nullptr);
+        initProp(property);
       }
 
       // Initialize per-configuration name postfix property from the
@@ -388,7 +395,7 @@
           impl->TargetType != cmStateEnums::INTERFACE_LIBRARY) {
         std::string property = cmSystemTools::UpperCase(configName);
         property += "_POSTFIX";
-        InitProperty(property, nullptr);
+        initProp(property);
       }
     }
   }
@@ -427,16 +434,16 @@
 
   if (this->GetType() != cmStateEnums::INTERFACE_LIBRARY &&
       this->GetType() != cmStateEnums::UTILITY) {
-    InitProperty("C_VISIBILITY_PRESET", nullptr);
-    InitProperty("CXX_VISIBILITY_PRESET", nullptr);
-    InitProperty("CUDA_VISIBILITY_PRESET", nullptr);
-    InitProperty("VISIBILITY_INLINES_HIDDEN", nullptr);
+    initProp("C_VISIBILITY_PRESET");
+    initProp("CXX_VISIBILITY_PRESET");
+    initProp("CUDA_VISIBILITY_PRESET");
+    initProp("VISIBILITY_INLINES_HIDDEN");
   }
 
   if (impl->TargetType == cmStateEnums::EXECUTABLE) {
-    InitProperty("ANDROID_GUI", nullptr);
-    InitProperty("CROSSCOMPILING_EMULATOR", nullptr);
-    InitProperty("ENABLE_EXPORTS", nullptr);
+    initProp("ANDROID_GUI");
+    initProp("CROSSCOMPILING_EMULATOR");
+    initProp("ENABLE_EXPORTS");
   }
   if (impl->TargetType == cmStateEnums::SHARED_LIBRARY ||
       impl->TargetType == cmStateEnums::MODULE_LIBRARY) {
@@ -444,12 +451,12 @@
   }
   if (impl->TargetType == cmStateEnums::SHARED_LIBRARY ||
       impl->TargetType == cmStateEnums::EXECUTABLE) {
-    InitProperty("WINDOWS_EXPORT_ALL_SYMBOLS", nullptr);
+    initProp("WINDOWS_EXPORT_ALL_SYMBOLS");
   }
 
   if (this->GetType() != cmStateEnums::INTERFACE_LIBRARY &&
       this->GetType() != cmStateEnums::UTILITY) {
-    InitProperty("POSITION_INDEPENDENT_CODE", nullptr);
+    initProp("POSITION_INDEPENDENT_CODE");
   }
 
   // Record current policies for later use.
@@ -465,12 +472,12 @@
 
   if (this->GetType() != cmStateEnums::INTERFACE_LIBRARY &&
       this->GetType() != cmStateEnums::UTILITY) {
-    InitProperty("JOB_POOL_COMPILE", nullptr);
-    InitProperty("JOB_POOL_LINK", nullptr);
+    initProp("JOB_POOL_COMPILE");
+    initProp("JOB_POOL_LINK");
   }
 
   if (impl->TargetType <= cmStateEnums::UTILITY) {
-    InitProperty("DOTNET_TARGET_FRAMEWORK_VERSION", nullptr);
+    initProp("DOTNET_TARGET_FRAMEWORK_VERSION");
   }
 
   if (this->GetType() != cmStateEnums::INTERFACE_LIBRARY &&
@@ -491,7 +498,7 @@
           if (assignment != std::string::npos) {
             const std::string propName = vsGlobal + i.substr(0, assignment);
             const std::string propValue = i.substr(assignment + 1);
-            InitProperty(propName, propValue.c_str());
+            initPropValue(propName, propValue.c_str());
           }
         }
       }
@@ -1484,7 +1491,6 @@
 
 const char* cmTarget::GetProperty(const std::string& prop) const
 {
-  static std::unordered_set<std::string> specialProps;
 #define MAKE_STATIC_PROP(PROP) static const std::string prop##PROP = #PROP
   MAKE_STATIC_PROP(LINK_LIBRARIES);
   MAKE_STATIC_PROP(TYPE);
@@ -1502,23 +1508,23 @@
   MAKE_STATIC_PROP(SOURCE_DIR);
   MAKE_STATIC_PROP(SOURCES);
 #undef MAKE_STATIC_PROP
-  if (specialProps.empty()) {
-    specialProps.insert(propLINK_LIBRARIES);
-    specialProps.insert(propTYPE);
-    specialProps.insert(propINCLUDE_DIRECTORIES);
-    specialProps.insert(propCOMPILE_FEATURES);
-    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);
-    specialProps.insert(propNAME);
-    specialProps.insert(propBINARY_DIR);
-    specialProps.insert(propSOURCE_DIR);
-    specialProps.insert(propSOURCES);
-  }
+  static std::unordered_set<std::string> const specialProps{
+    propLINK_LIBRARIES,
+    propTYPE,
+    propINCLUDE_DIRECTORIES,
+    propCOMPILE_FEATURES,
+    propCOMPILE_OPTIONS,
+    propCOMPILE_DEFINITIONS,
+    propLINK_OPTIONS,
+    propLINK_DIRECTORIES,
+    propIMPORTED,
+    propIMPORTED_GLOBAL,
+    propMANUALLY_ADDED_DEPENDENCIES,
+    propNAME,
+    propBINARY_DIR,
+    propSOURCE_DIR,
+    propSOURCES
+  };
   if (specialProps.count(prop)) {
     if (prop == propLINK_LIBRARIES) {
       if (impl->LinkImplementationPropertyEntries.empty()) {
diff --git a/Source/cmTargetPropertyComputer.cxx b/Source/cmTargetPropertyComputer.cxx
index 3f763af..eac300f 100644
--- a/Source/cmTargetPropertyComputer.cxx
+++ b/Source/cmTargetPropertyComputer.cxx
@@ -56,22 +56,21 @@
   if (std::islower(prop[0])) {
     return true;
   }
-  static std::unordered_set<std::string> builtIns;
-  if (builtIns.empty()) {
-    builtIns.insert("COMPATIBLE_INTERFACE_BOOL");
-    builtIns.insert("COMPATIBLE_INTERFACE_NUMBER_MAX");
-    builtIns.insert("COMPATIBLE_INTERFACE_NUMBER_MIN");
-    builtIns.insert("COMPATIBLE_INTERFACE_STRING");
-    builtIns.insert("EXPORT_NAME");
-    builtIns.insert("EXPORT_PROPERTIES");
-    builtIns.insert("IMPORTED");
-    builtIns.insert("IMPORTED_GLOBAL");
-    builtIns.insert("MANUALLY_ADDED_DEPENDENCIES");
-    builtIns.insert("NAME");
-    builtIns.insert("PRIVATE_HEADER");
-    builtIns.insert("PUBLIC_HEADER");
-    builtIns.insert("TYPE");
-  }
+  static std::unordered_set<std::string> const builtIns{
+    "COMPATIBLE_INTERFACE_BOOL",
+    "COMPATIBLE_INTERFACE_NUMBER_MAX",
+    "COMPATIBLE_INTERFACE_NUMBER_MIN",
+    "COMPATIBLE_INTERFACE_STRING",
+    "EXPORT_NAME",
+    "EXPORT_PROPERTIES",
+    "IMPORTED",
+    "IMPORTED_GLOBAL",
+    "MANUALLY_ADDED_DEPENDENCIES",
+    "NAME",
+    "PRIVATE_HEADER",
+    "PUBLIC_HEADER",
+    "TYPE"
+  };
 
   if (builtIns.count(prop)) {
     return true;
diff --git a/Source/cmTest.cxx b/Source/cmTest.cxx
index 7d45cf5..01f2b96 100644
--- a/Source/cmTest.cxx
+++ b/Source/cmTest.cxx
@@ -8,7 +8,8 @@
 #include "cmSystemTools.h"
 
 cmTest::cmTest(cmMakefile* mf)
-  : Backtrace(mf->GetBacktrace())
+  : CommandExpandLists(false)
+  , Backtrace(mf->GetBacktrace())
 {
   this->Makefile = mf;
   this->OldStyle = true;
@@ -59,3 +60,13 @@
 {
   this->Properties.AppendProperty(prop, value, asString);
 }
+
+bool cmTest::GetCommandExpandLists() const
+{
+  return this->CommandExpandLists;
+}
+
+void cmTest::SetCommandExpandLists(bool b)
+{
+  this->CommandExpandLists = b;
+}
diff --git a/Source/cmTest.h b/Source/cmTest.h
index 88dc730..02d8f46 100644
--- a/Source/cmTest.h
+++ b/Source/cmTest.h
@@ -51,10 +51,15 @@
   bool GetOldStyle() const { return this->OldStyle; }
   void SetOldStyle(bool b) { this->OldStyle = b; }
 
+  /** Set/Get whether lists in command lines should be expanded. */
+  bool GetCommandExpandLists() const;
+  void SetCommandExpandLists(bool b);
+
 private:
   cmPropertyMap Properties;
   std::string Name;
   std::vector<std::string> Command;
+  bool CommandExpandLists;
 
   bool OldStyle;
 
diff --git a/Source/cmTestGenerator.cxx b/Source/cmTestGenerator.cxx
index 571cd09..498953e 100644
--- a/Source/cmTestGenerator.cxx
+++ b/Source/cmTestGenerator.cxx
@@ -10,7 +10,6 @@
 #include "cmListFileCache.h"
 #include "cmLocalGenerator.h"
 #include "cmOutputConverter.h"
-#include "cmProperty.h"
 #include "cmPropertyMap.h"
 #include "cmRange.h"
 #include "cmStateTypes.h"
@@ -76,12 +75,22 @@
   // Start the test command.
   os << indent << "add_test(" << this->Test->GetName() << " ";
 
-  // Get the test command line to be executed.
-  std::vector<std::string> const& command = this->Test->GetCommand();
+  // Evaluate command line arguments
+  std::vector<std::string> argv =
+    EvaluateCommandLineArguments(this->Test->GetCommand(), ge, config);
+
+  // Expand arguments if COMMAND_EXPAND_LISTS is set
+  if (this->Test->GetCommandExpandLists()) {
+    argv = cmSystemTools::ExpandedLists(argv.begin(), argv.end());
+    // Expanding lists on an empty command may have left it empty
+    if (argv.empty()) {
+      argv.emplace_back();
+    }
+  }
 
   // Check whether the command executable is a target whose name is to
   // be translated.
-  std::string exe = command[0];
+  std::string exe = argv[0];
   cmGeneratorTarget* target = this->LG->FindGeneratorTargetToUse(exe);
   if (target && target->GetType() == cmStateEnums::EXECUTABLE) {
     // Use the target file on disk.
@@ -101,29 +110,26 @@
     }
   } else {
     // Use the command name given.
-    exe = ge.Parse(exe)->Evaluate(this->LG, config);
     cmSystemTools::ConvertToUnixSlashes(exe);
   }
 
   // Generate the command line with full escapes.
   os << cmOutputConverter::EscapeForCMake(exe);
-  for (std::string const& arg : cmMakeRange(command).advance(1)) {
-    os << " "
-       << cmOutputConverter::EscapeForCMake(
-            ge.Parse(arg)->Evaluate(this->LG, config));
+
+  for (auto const& arg : cmMakeRange(argv).advance(1)) {
+    os << " " << cmOutputConverter::EscapeForCMake(arg);
   }
 
   // Finish the test command.
   os << ")\n";
 
   // Output properties for the test.
-  cmPropertyMap& pm = this->Test->GetProperties();
   os << indent << "set_tests_properties(" << this->Test->GetName()
      << " PROPERTIES ";
-  for (auto const& i : pm) {
+  for (auto const& i : this->Test->GetProperties().GetList()) {
     os << " " << i.first << " "
        << cmOutputConverter::EscapeForCMake(
-            ge.Parse(i.second.GetValue())->Evaluate(this->LG, config));
+            ge.Parse(i.second)->Evaluate(this->LG, config));
   }
   this->GenerateInternalProperties(os);
   os << ")" << std::endl;
@@ -173,12 +179,11 @@
   fout << ")" << std::endl;
 
   // Output properties for the test.
-  cmPropertyMap& pm = this->Test->GetProperties();
   fout << indent << "set_tests_properties(" << this->Test->GetName()
        << " PROPERTIES ";
-  for (auto const& i : pm) {
+  for (auto const& i : this->Test->GetProperties().GetList()) {
     fout << " " << i.first << " "
-         << cmOutputConverter::EscapeForCMake(i.second.GetValue());
+         << cmOutputConverter::EscapeForCMake(i.second);
   }
   this->GenerateInternalProperties(fout);
   fout << ")" << std::endl;
@@ -208,3 +213,16 @@
 
   os << "\"";
 }
+
+std::vector<std::string> cmTestGenerator::EvaluateCommandLineArguments(
+  const std::vector<std::string>& argv, cmGeneratorExpression& ge,
+  const std::string& config) const
+{
+  // Evaluate executable name and arguments
+  auto evaluatedRange =
+    cmMakeRange(argv).transform([&](const std::string& arg) {
+      return ge.Parse(arg)->Evaluate(this->LG, config);
+    });
+
+  return { evaluatedRange.begin(), evaluatedRange.end() };
+}
diff --git a/Source/cmTestGenerator.h b/Source/cmTestGenerator.h
index 8b9cf78..7ac68eb 100644
--- a/Source/cmTestGenerator.h
+++ b/Source/cmTestGenerator.h
@@ -11,6 +11,7 @@
 #include <string>
 #include <vector>
 
+class cmGeneratorExpression;
 class cmLocalGenerator;
 class cmTest;
 
@@ -38,6 +39,9 @@
 
 private:
   void GenerateInternalProperties(std::ostream& os);
+  std::vector<std::string> EvaluateCommandLineArguments(
+    const std::vector<std::string>& argv, cmGeneratorExpression& ge,
+    const std::string& config) const;
 
 protected:
   void GenerateScriptConfigs(std::ostream& os, Indent indent) override;
diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx
index 9368414..634c990 100644
--- a/Source/cmVisualStudio10TargetGenerator.cxx
+++ b/Source/cmVisualStudio10TargetGenerator.cxx
@@ -774,11 +774,11 @@
     cmSystemTools::ExpandListArgument(vsDotNetReferences, references);
   }
   cmPropertyMap const& props = this->GeneratorTarget->Target->GetProperties();
-  for (auto const& i : props) {
+  for (auto const& i : props.GetList()) {
     if (i.first.find("VS_DOTNET_REFERENCE_") == 0) {
       std::string name = i.first.substr(20);
       if (!name.empty()) {
-        std::string path = i.second.GetValue();
+        std::string path = i.second;
         if (!cmsys::SystemTools::FileIsFullPath(path)) {
           path = this->Makefile->GetCurrentSourceDirectory() + "/" + path;
         }
@@ -870,10 +870,10 @@
   typedef std::map<std::string, std::string> CustomTags;
   CustomTags tags;
   cmPropertyMap const& props = this->GeneratorTarget->Target->GetProperties();
-  for (const auto& i : props) {
+  for (const auto& i : props.GetList()) {
     if (i.first.find(refPropFullPrefix) == 0) {
       std::string refTag = i.first.substr(refPropFullPrefix.length());
-      std::string refVal = i.second.GetValue();
+      std::string refVal = i.second;
       if (!refTag.empty() && !refVal.empty()) {
         tags[refTag] = refVal;
       }
@@ -967,12 +967,12 @@
           }
         }
         const cmPropertyMap& props = oi->GetProperties();
-        for (const auto& p : props) {
+        for (const std::string& p : props.GetKeys()) {
           static const std::string propNamePrefix = "VS_CSHARP_";
-          if (p.first.find(propNamePrefix) == 0) {
-            std::string tagName = p.first.substr(propNamePrefix.length());
+          if (p.find(propNamePrefix) == 0) {
+            std::string tagName = p.substr(propNamePrefix.length());
             if (!tagName.empty()) {
-              std::string value = props.GetPropertyValue(p.first);
+              std::string value = props.GetPropertyValue(p);
               if (!value.empty()) {
                 e2.Element(tagName.c_str(), value);
               }
@@ -4681,12 +4681,12 @@
 {
   if (this->ProjectType == csproj) {
     const cmPropertyMap& props = sf->GetProperties();
-    for (auto const& p : props) {
+    for (const std::string& p : props.GetKeys()) {
       static const std::string propNamePrefix = "VS_CSHARP_";
-      if (p.first.find(propNamePrefix) == 0) {
-        std::string tagName = p.first.substr(propNamePrefix.length());
+      if (p.find(propNamePrefix) == 0) {
+        std::string tagName = p.substr(propNamePrefix.length());
         if (!tagName.empty()) {
-          const std::string val = props.GetPropertyValue(p.first);
+          const std::string val = props.GetPropertyValue(p);
           if (!val.empty()) {
             tags[tagName] = val;
           } else {
diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt
index 69f8162..735ad5f 100644
--- a/Tests/RunCMake/CMakeLists.txt
+++ b/Tests/RunCMake/CMakeLists.txt
@@ -408,7 +408,12 @@
   set(NO_NAMELINK 0)
 endif()
 
-add_RunCMake_test(install -DNO_NAMELINK=${NO_NAMELINK} -DCYGWIN=${CYGWIN})
+add_RunCMake_test(install -DNO_NAMELINK=${NO_NAMELINK} -DCYGWIN=${CYGWIN} -DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID}
+  -DCMAKE_SHARED_LIBRARY_RPATH_ORIGIN_TOKEN=${CMAKE_SHARED_LIBRARY_RPATH_ORIGIN_TOKEN}
+  -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME}
+  -DCMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG=${CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG}
+  -DCMAKE_EXECUTABLE_FORMAT=${CMAKE_EXECUTABLE_FORMAT})
+
 add_RunCMake_test(CPackCommandLine)
 add_RunCMake_test(CPackConfig)
 add_RunCMake_test(CPackInstallProperties)
@@ -565,3 +570,5 @@
   add_RunCMake_test(CSharpCustomCommand)
   add_RunCMake_test(CSharpReferenceImport)
 endif()
+
+add_RunCMake_test("CTestCommandExpandLists")
diff --git a/Tests/RunCMake/CTestCommandExpandLists/CMakeLists.txt b/Tests/RunCMake/CTestCommandExpandLists/CMakeLists.txt
new file mode 100644
index 0000000..3e470a2
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandExpandLists/CMakeLists.txt
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.14)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/CTestCommandExpandLists/CMakeLists.txt.in b/Tests/RunCMake/CTestCommandExpandLists/CMakeLists.txt.in
new file mode 100644
index 0000000..7d56c90
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandExpandLists/CMakeLists.txt.in
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.14)
+project(@CASE_NAME@ NONE)
+include("@RunCMake_SOURCE_DIR@/@CASE_NAME@.cmake")
diff --git a/Tests/RunCMake/CTestCommandExpandLists/RunCMakeTest.cmake b/Tests/RunCMake/CTestCommandExpandLists/RunCMakeTest.cmake
new file mode 100644
index 0000000..7c3779e
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandExpandLists/RunCMakeTest.cmake
@@ -0,0 +1,5 @@
+include(RunCTest)
+
+run_ctest(expandGeneratorExpressionResult)
+run_ctest(expandEmptyCommand)
+run_cmake(multipleExpandOptions)
diff --git a/Tests/RunCMake/CTestCommandExpandLists/compare_options.cmake b/Tests/RunCMake/CTestCommandExpandLists/compare_options.cmake
new file mode 100644
index 0000000..a32e579
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandExpandLists/compare_options.cmake
@@ -0,0 +1,14 @@
+set(range 1 2 3 4 5 6 7 8 9 10)
+set(aargs "")
+set(bargs "")
+foreach(n IN LISTS range)
+  set(aval "${A${n}ARG}")
+  set(bval "${B${n}ARG}")
+  if(aval OR bval)
+    list(APPEND aargs "\"${aval}\"")
+    list(APPEND bargs "\"${bval}\"")
+  endif()
+endforeach()
+if(NOT "${aargs}" STREQUAL "${bargs}")
+  message(FATAL_ERROR "COMPARE_OPTIONS: \n\t${aargs} != \n\t${bargs}")
+endif()
diff --git a/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand-result.txt b/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand-result.txt
new file mode 100644
index 0000000..b57e2de
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand-result.txt
@@ -0,0 +1 @@
+(-1|255)
diff --git a/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand-stderr.txt b/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand-stderr.txt
new file mode 100644
index 0000000..c656b4c
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand-stderr.txt
@@ -0,0 +1 @@
+Unable to find executable:
diff --git a/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand-stdout.txt b/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand-stdout.txt
new file mode 100644
index 0000000..0752580
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand-stdout.txt
@@ -0,0 +1,13 @@
+Test project .*/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand-build
+.* +Start 1: CommandExpandEmptyList
+Could not find executable +
+Looked in the following places:
+.*
+1/1 Test #1: CommandExpandEmptyList +\.+\*\*\*Not Run +[0-9.]+ sec
++
+0% tests passed, 1 tests failed out of 1
++
+Total Test time \(real\) = +[0-9.]+ sec
++
+The following tests FAILED:
+.* +1 - CommandExpandEmptyList \(Not Run\)$
diff --git a/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand.cmake b/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand.cmake
new file mode 100644
index 0000000..b75828e
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand.cmake
@@ -0,0 +1,10 @@
+include(CTest)
+
+set(argv /bin/true)
+list(POP_BACK argv)
+
+add_test(
+  NAME CommandExpandEmptyList
+  COMMAND "$<JOIN:${argv},;>"
+  COMMAND_EXPAND_LISTS
+)
diff --git a/Tests/RunCMake/CTestCommandExpandLists/expandGeneratorExpressionResult-result.txt b/Tests/RunCMake/CTestCommandExpandLists/expandGeneratorExpressionResult-result.txt
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandExpandLists/expandGeneratorExpressionResult-result.txt
@@ -0,0 +1 @@
+0
diff --git a/Tests/RunCMake/CTestCommandExpandLists/expandGeneratorExpressionResult-stdout.txt b/Tests/RunCMake/CTestCommandExpandLists/expandGeneratorExpressionResult-stdout.txt
new file mode 100644
index 0000000..2f21592
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandExpandLists/expandGeneratorExpressionResult-stdout.txt
@@ -0,0 +1,7 @@
+Test project .*/Tests/RunCMake/CTestCommandExpandLists/expandGeneratorExpressionResult-build
+.* +Start 1: CommandExpandList
+1/1 Test #1: CommandExpandList +\.+ +Passed +[0-9.]+ sec
++
+100% tests passed, 0 tests failed out of 1
++
+Total Test time \(real\) = +[0-9.]+ sec
diff --git a/Tests/RunCMake/CTestCommandExpandLists/expandGeneratorExpressionResult.cmake b/Tests/RunCMake/CTestCommandExpandLists/expandGeneratorExpressionResult.cmake
new file mode 100644
index 0000000..20608ae
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandExpandLists/expandGeneratorExpressionResult.cmake
@@ -0,0 +1,19 @@
+include(CTest)
+
+
+set(cmp_args "1ARG=COMMAND_EXPAND_LISTS" "2ARG=test" "3ARG=outfile"
+  "4ARG=content")
+set(AARGS "")
+foreach(arg IN LISTS cmp_args)
+  list(APPEND AARGS "-DA${arg}")
+endforeach()
+
+
+
+add_test(
+  NAME CommandExpandList
+  COMMAND ${CMAKE_COMMAND} ${AARGS} -V
+  "-DB$<JOIN:${cmp_args},;-DB>"
+  "-P" "${CMAKE_CURRENT_LIST_DIR}/compare_options.cmake"
+  COMMAND_EXPAND_LISTS
+)
diff --git a/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions-result.txt b/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions-stderr.txt b/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions-stderr.txt
new file mode 100644
index 0000000..e48513f
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions-stderr.txt
@@ -0,0 +1,2 @@
+CMake Error at multipleExpandOptions\.cmake:3 \(add_test\):
+ +add_test may be given at most one COMMAND_EXPAND_LISTS\.
diff --git a/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions-stdout.txt b/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions-stdout.txt
new file mode 100644
index 0000000..55bb894
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions-stdout.txt
@@ -0,0 +1,2 @@
+-- Configuring incomplete, errors occurred!
+See also ".*/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions-build/CMakeFiles/CMakeOutput\.log".
diff --git a/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions.cmake b/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions.cmake
new file mode 100644
index 0000000..dcf2dc4
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions.cmake
@@ -0,0 +1,8 @@
+include(CTest)
+
+add_test(
+  NAME MultipleExpandOptions
+  COMMAND /bin/true
+  COMMAND_EXPAND_LISTS
+  COMMAND_EXPAND_LISTS
+)
diff --git a/Tests/RunCMake/CTestCommandExpandLists/test.cmake.in b/Tests/RunCMake/CTestCommandExpandLists/test.cmake.in
new file mode 100644
index 0000000..d9a8ccb
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandExpandLists/test.cmake.in
@@ -0,0 +1,15 @@
+cmake_minimum_required(VERSION 3.14)
+
+set(CTEST_SITE                          "test-site")
+set(CTEST_BUILD_NAME                    "test-build-name")
+set(CTEST_SOURCE_DIRECTORY              "@RunCMake_BINARY_DIR@/@CASE_NAME@")
+set(CTEST_BINARY_DIRECTORY              "@RunCMake_BINARY_DIR@/@CASE_NAME@-build")
+set(CTEST_CMAKE_GENERATOR               "@RunCMake_GENERATOR@")
+set(CTEST_CMAKE_GENERATOR_PLATFORM      "@RunCMake_GENERATOR_PLATFORM@")
+set(CTEST_CMAKE_GENERATOR_TOOLSET       "@RunCMake_GENERATOR_TOOLSET@")
+set(CTEST_BUILD_CONFIGURATION           "$ENV{CMAKE_CONFIG_TYPE}")
+
+ctest_start(Experimental)
+ctest_configure()
+ctest_build()
+ctest_test()
diff --git a/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt b/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt
index 0bcf886..6d72fac 100644
--- a/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt
+++ b/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt
@@ -27,6 +27,7 @@
    \* CMP0076
    \* CMP0081
    \* CMP0083
+   \* CMP0095
 
 Call Stack \(most recent call first\):
   CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/install/CMakeLists.txt b/Tests/RunCMake/install/CMakeLists.txt
index 6dd8cdf..c7e99ad 100644
--- a/Tests/RunCMake/install/CMakeLists.txt
+++ b/Tests/RunCMake/install/CMakeLists.txt
@@ -1,3 +1,6 @@
 cmake_minimum_required(VERSION 3.4)
+if(RunCMake_TEST MATCHES "^file-GET_RUNTIME_DEPENDENCIES")
+  cmake_policy(SET CMP0087 NEW)
+endif()
 project(${RunCMake_TEST} NONE)
 include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/install/RunCMakeTest.cmake b/Tests/RunCMake/install/RunCMakeTest.cmake
index c637db1..21c320b 100644
--- a/Tests/RunCMake/install/RunCMakeTest.cmake
+++ b/Tests/RunCMake/install/RunCMakeTest.cmake
@@ -48,6 +48,22 @@
   endif()
 endfunction()
 
+# Wrapper for run_cmake() that skips platforms that are non-ELF or have no RPATH support
+function(run_cmake_ELFRPATH_only case)
+  if(UNIX AND CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG AND CMAKE_EXECUTABLE_FORMAT STREQUAL "ELF")
+    run_cmake(${case})
+  else()
+    # Sanity check against a platform known to be ELF-based
+    if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
+      message(FATAL_ERROR "Expected platform Linux to advertize itself as ELF-based, but it did not.")
+    else()
+      message(STATUS "${case} - SKIPPED (No ELF-based platform found)")
+    endif()
+  endif()
+endfunction()
+
+run_cmake(TARGETS-FILE_RPATH_CHANGE-old_rpath)
+run_cmake_ELFRPATH_only(TARGETS-FILE_RPATH_CHANGE-new_rpath)
 run_cmake(DIRECTORY-MESSAGE_NEVER)
 run_cmake(DIRECTORY-PATTERN-MESSAGE_NEVER)
 run_cmake(DIRECTORY-message)
@@ -139,6 +155,36 @@
 run_install_test(TARGETS-RPATH)
 run_install_test(InstallRequiredSystemLibraries)
 
+if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin")
+  run_install_test(file-GET_RUNTIME_DEPENDENCIES-macos)
+  run_install_test(file-GET_RUNTIME_DEPENDENCIES-macos-unresolved)
+  run_install_test(file-GET_RUNTIME_DEPENDENCIES-macos-conflict)
+  run_install_test(file-GET_RUNTIME_DEPENDENCIES-macos-notfile)
+  run_cmake(file-GET_RUNTIME_DEPENDENCIES-project)
+  run_cmake(file-GET_RUNTIME_DEPENDENCIES-badargs1)
+  run_cmake(file-GET_RUNTIME_DEPENDENCIES-badargs2)
+elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows")
+  run_install_test(file-GET_RUNTIME_DEPENDENCIES-windows)
+  run_install_test(file-GET_RUNTIME_DEPENDENCIES-windows-unresolved)
+  run_install_test(file-GET_RUNTIME_DEPENDENCIES-windows-conflict)
+  run_install_test(file-GET_RUNTIME_DEPENDENCIES-windows-notfile)
+  run_cmake(file-GET_RUNTIME_DEPENDENCIES-project)
+  run_cmake(file-GET_RUNTIME_DEPENDENCIES-badargs1)
+  run_cmake(file-GET_RUNTIME_DEPENDENCIES-badargs2)
+elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux")
+  if(NOT CMAKE_C_COMPILER_ID MATCHES "^XL")
+    run_install_test(file-GET_RUNTIME_DEPENDENCIES-linux)
+  endif()
+  run_install_test(file-GET_RUNTIME_DEPENDENCIES-linux-unresolved)
+  run_install_test(file-GET_RUNTIME_DEPENDENCIES-linux-conflict)
+  run_install_test(file-GET_RUNTIME_DEPENDENCIES-linux-notfile)
+  run_cmake(file-GET_RUNTIME_DEPENDENCIES-project)
+  run_cmake(file-GET_RUNTIME_DEPENDENCIES-badargs1)
+  run_cmake(file-GET_RUNTIME_DEPENDENCIES-badargs2)
+else()
+  run_cmake(file-GET_RUNTIME_DEPENDENCIES-unsupported)
+endif()
+
 set(run_install_test_components 1)
 run_install_test(FILES-EXCLUDE_FROM_ALL)
 run_install_test(TARGETS-EXCLUDE_FROM_ALL)
diff --git a/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-check-common.cmake b/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-check-common.cmake
new file mode 100644
index 0000000..673fdde
--- /dev/null
+++ b/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-check-common.cmake
@@ -0,0 +1,30 @@
+file(READ ${RunCMake_TEST_BINARY_DIR}/cmake_install.cmake install_script)
+#message(STATUS ${install_script})
+
+set(wsnl " *[\n\r]+ *") # whitespace + single newline + whitespace
+set(wssl " *[\n\r]+[^\n\r]*[\n\r]+ *") # ws nl skipline nl ws
+string(CONCAT prefix [[file\(RPATH_CHANGE]])
+set(_msg "cmake_install.cmake does not match ")
+
+macro(check)
+  if(NOT install_script MATCHES "${regex}")
+    message(STATUS "${test} - check \"${target}\" - FAILED:")
+    string(CONCAT RunCMake_TEST_FAILED "${_msg}" ">>>${regex}<<<")
+    return()
+  else()
+    message(STATUS "${test} - check \"${target}\" - PASSED")
+  endif()
+endmacro()
+
+macro(skip_without_rpath_change_rule)
+# Not all platforms generate a file(RPATH_CHANGE) rule
+  if(NOT install_script MATCHES [[file\(RPATH_CHANGE]])
+    # Sanity check against a platform known to generate a file(RPATH_CHANGE) rule
+    if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
+      message(FATAL_ERROR "Expected generated file(RPATH_CHANGE) rule on platform Linux.")
+    else()
+      message(STATUS "${test} - All checks skipped. No file(RPATH_CHANGE) rule found on this platform.")
+      return()
+    endif()
+  endif()
+endmacro()
diff --git a/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-new_rpath-check.cmake b/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-new_rpath-check.cmake
new file mode 100644
index 0000000..930ef70
--- /dev/null
+++ b/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-new_rpath-check.cmake
@@ -0,0 +1,63 @@
+include(${RunCMake_SOURCE_DIR}/TARGETS-FILE_RPATH_CHANGE-check-common.cmake)
+skip_without_rpath_change_rule()
+string(APPEND prefix "${wsnl}" [[FILE "[^"]*/]])
+
+set(target "exe1_cmp0095_old")
+string(CONCAT regex "${prefix}${target}\"${wssl}"
+              [[NEW_RPATH "/foo/bar]])
+check()
+
+set(target "exe1_cmp0095_warn")
+string(CONCAT regex "${prefix}${target}\"${wssl}"
+              [[NEW_RPATH "/foo/bar]])
+check()
+
+set(target "exe1_cmp0095_new")
+string(CONCAT regex "${prefix}${target}\"${wssl}"
+              [[NEW_RPATH "/foo/bar]])
+check()
+
+set(target "exe2_cmp0095_old")
+string(CONCAT regex "${prefix}${target}\"${wssl}"
+              [[NEW_RPATH "\$ORIGIN/../lib]])
+check()
+
+set(target "exe2_cmp0095_warn")
+string(CONCAT regex "${prefix}${target}\"${wssl}"
+              [[NEW_RPATH "\$ORIGIN/../lib]])
+check()
+
+set(target "exe2_cmp0095_new")
+string(CONCAT regex "${prefix}${target}\"${wssl}"
+              [[NEW_RPATH "\\\$ORIGIN/../lib]])
+check()
+
+set(target "exe3_cmp0095_old")
+string(CONCAT regex "${prefix}${target}\"${wssl}"
+              [[NEW_RPATH "\${ORIGIN}/../lib]])
+check()
+
+set(target "exe3_cmp0095_warn")
+string(CONCAT regex "${prefix}${target}\"${wssl}"
+              [[NEW_RPATH "\${ORIGIN}/../lib]])
+check()
+
+set(target "exe3_cmp0095_new")
+string(CONCAT regex "${prefix}${target}\"${wssl}"
+              [[NEW_RPATH "\\\${ORIGIN}/../lib]])
+check()
+
+set(target "exe4_cmp0095_old")
+string(CONCAT regex "${prefix}${target}\"${wssl}"
+              [[NEW_RPATH "/foo/bar/\${PLATFORM}]])
+check()
+
+set(target "exe4_cmp0095_warn")
+string(CONCAT regex "${prefix}${target}\"${wssl}"
+              [[NEW_RPATH "/foo/bar/\${PLATFORM}]])
+check()
+
+set(target "exe4_cmp0095_new")
+string(CONCAT regex "${prefix}${target}\"${wssl}"
+              [[NEW_RPATH "/foo/bar/\\\${PLATFORM}]])
+check()
diff --git a/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-new_rpath-stderr.txt b/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-new_rpath-stderr.txt
new file mode 100644
index 0000000..1e123f6
--- /dev/null
+++ b/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-new_rpath-stderr.txt
@@ -0,0 +1,23 @@
+^CMake Warning \(dev\) at TARGETS-FILE_RPATH_CHANGE-new_rpath\.cmake:[0-9]+ \(install\):
+  Policy CMP0095 is not set: RPATH entries are properly escaped in the
+  intermediary CMake install script\.  Run "cmake --help-policy CMP0095" for
+  policy details\.  Use the cmake_policy command to set the policy and
+  suppress this warning\.
+
+  RPATH entries for target 'exe3_cmp0095_warn' will not be escaped in the
+  intermediary cmake_install\.cmake script\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)
+This warning is for project developers\.  Use -Wno-dev to suppress it\.
+
+CMake Warning \(dev\) at TARGETS-FILE_RPATH_CHANGE-new_rpath\.cmake:[0-9]+ \(install\):
+  Policy CMP0095 is not set: RPATH entries are properly escaped in the
+  intermediary CMake install script\.  Run "cmake --help-policy CMP0095" for
+  policy details\.  Use the cmake_policy command to set the policy and
+  suppress this warning\.
+
+  RPATH entries for target 'exe4_cmp0095_warn' will not be escaped in the
+  intermediary cmake_install\.cmake script\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)
+This warning is for project developers\.  Use -Wno-dev to suppress it\.$
diff --git a/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-new_rpath.cmake b/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-new_rpath.cmake
new file mode 100644
index 0000000..cba04b2
--- /dev/null
+++ b/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-new_rpath.cmake
@@ -0,0 +1,72 @@
+cmake_minimum_required(VERSION 3.14)
+enable_language(C)
+
+# test matrix
+#
+# A :=
+#                        | no cmake syntax | cmake syntax |
+# -----------------------+-----------------+--------------+
+# absolute install RPATH |       exe1      |     exe4     |
+# relative install RPATH |       exe2      |     exe3     |
+#
+# all := A * CMP005_OLD + A * CMP0095_WARN + A * CMP0095_NEW
+
+add_library(utils SHARED obj1.c)
+set(targets utils)
+
+set(exe1_install_rpath "/foo/bar")
+set(exe2_install_rpath "\$ORIGIN/../lib")
+set(exe3_install_rpath "\${ORIGIN}/../lib")
+set(exe4_install_rpath "/foo/bar/\${PLATFORM}")
+
+macro(A_CMP0095 policy_value)
+  cmake_policy(PUSH)
+  if(NOT "x${policy_value}x" STREQUAL "xWARNx")
+    cmake_policy(SET CMP0095 ${policy_value})
+  endif()
+  string(TOLOWER "${policy_value}" p)
+
+  # exe1: absolute install RPATH, no cmake syntax
+  set(case "exe1")
+  set(target "${case}_cmp0095_${p}")
+  list(APPEND targets ${target})
+  add_executable(${target} main.c)
+  target_link_libraries(${target} PRIVATE utils)
+  set_target_properties(${target} PROPERTIES
+    INSTALL_RPATH "${${case}_install_rpath}")
+
+  # exe2: relative install RPATH, no cmake syntax
+  set(case "exe2")
+  set(target "${case}_cmp0095_${p}")
+  list(APPEND targets ${target})
+  add_executable(${target} main.c)
+  target_link_libraries(${target} PRIVATE utils)
+  set_target_properties(${target} PROPERTIES
+    INSTALL_RPATH "${${case}_install_rpath}")
+
+  # exe3: relative install RPATH, cmake syntax
+  set(case "exe3")
+  set(target "${case}_cmp0095_${p}")
+  list(APPEND targets ${target})
+  add_executable(${target} main.c)
+  target_link_libraries(${target} PRIVATE utils)
+  set_target_properties(${target} PROPERTIES
+    INSTALL_RPATH "${${case}_install_rpath}")
+
+  # exe4: absolute install RPATH, cmake syntax
+  set(case "exe4")
+  set(target "${case}_cmp0095_${p}")
+  list(APPEND targets ${target})
+  add_executable(${target} main.c)
+  target_link_libraries(${target} PRIVATE utils)
+  set_target_properties(${target} PROPERTIES
+    INSTALL_RPATH "${${case}_install_rpath}")
+
+  cmake_policy(POP)
+endmacro()
+
+A_CMP0095("OLD")
+A_CMP0095("WARN") # exe3 and exe4 are expected to issue an author warning
+A_CMP0095("NEW")
+
+install(TARGETS ${targets})
diff --git a/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-old_rpath-check.cmake b/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-old_rpath-check.cmake
new file mode 100644
index 0000000..814f405
--- /dev/null
+++ b/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-old_rpath-check.cmake
@@ -0,0 +1,15 @@
+include(${RunCMake_SOURCE_DIR}/TARGETS-FILE_RPATH_CHANGE-check-common.cmake)
+skip_without_rpath_change_rule()
+string(APPEND prefix "${wsnl}" [[FILE "[^"]*/]])
+
+set(target "exe1")
+string(CONCAT regex "${prefix}${target}\"${wsnl}"
+              [[OLD_RPATH "]] "${RunCMake_BINARY_DIR}")
+check()
+
+if("x${CMAKE_SHARED_LIBRARY_RPATH_ORIGIN_TOKEN}" STREQUAL "x\$ORIGIN")
+  set(target "exe2")
+  string(CONCAT regex "${prefix}${target}\"${wsnl}"
+                [[OLD_RPATH "\\\$ORIGIN]])
+  check()
+endif()
diff --git a/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-old_rpath.cmake b/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-old_rpath.cmake
new file mode 100644
index 0000000..43ae787
--- /dev/null
+++ b/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-old_rpath.cmake
@@ -0,0 +1,18 @@
+cmake_minimum_required(VERSION 3.14)
+enable_language(C)
+
+add_library(utils SHARED obj1.c)
+
+# exe1: absolute build RPATH, no cmake syntax
+set(CMAKE_BUILD_RPATH_USE_ORIGIN OFF)
+set(CMAKE_INSTALL_RPATH "/foo/bar")
+add_executable(exe1 main.c)
+target_link_libraries(exe1 PRIVATE utils)
+
+# exe2: relative build RPATH, no cmake syntax
+set(CMAKE_BUILD_RPATH_USE_ORIGIN ON)
+set(CMAKE_INSTALL_RPATH "/foo/bar")
+add_executable(exe2 main.c)
+target_link_libraries(exe2 PRIVATE utils)
+
+install(TARGETS utils exe1 exe2)
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs1-result.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs1-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs1-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs1-stderr.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs1-stderr.txt
new file mode 100644
index 0000000..b66d1fe
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs1-stderr.txt
@@ -0,0 +1,18 @@
+^CMake Warning \(dev\) at file-GET_RUNTIME_DEPENDENCIES-badargs1\.cmake:[0-9]+ \(file\):
+  You have used file\(GET_RUNTIME_DEPENDENCIES\) in project mode\.  This is
+  probably not what you intended to do\.  Instead, please consider using it in
+  an install\(CODE\) or install\(SCRIPT\) command\.  For example:
+
+    install\(CODE \[\[
+      file\(GET_RUNTIME_DEPENDENCIES
+        # \.\.\.
+        \)
+      ]]\)
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)
+This warning is for project developers\.  Use -Wno-dev to suppress it\.
+
+CMake Error at file-GET_RUNTIME_DEPENDENCIES-badargs1\.cmake:[0-9]+ \(file\):
+  file Unrecognized argument: "invalid"
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs1.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs1.cmake
new file mode 100644
index 0000000..f3b8ce4
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs1.cmake
@@ -0,0 +1,2 @@
+file(GET_RUNTIME_DEPENDENCIES invalid)
+message(FATAL_ERROR "This message should not be displayed")
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs2-result.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs2-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs2-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs2-stderr.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs2-stderr.txt
new file mode 100644
index 0000000..94f0f46
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs2-stderr.txt
@@ -0,0 +1,18 @@
+^CMake Warning \(dev\) at file-GET_RUNTIME_DEPENDENCIES-badargs2\.cmake:[0-9]+ \(file\):
+  You have used file\(GET_RUNTIME_DEPENDENCIES\) in project mode\.  This is
+  probably not what you intended to do\.  Instead, please consider using it in
+  an install\(CODE\) or install\(SCRIPT\) command\.  For example:
+
+    install\(CODE \[\[
+      file\(GET_RUNTIME_DEPENDENCIES
+        # \.\.\.
+        \)
+      ]]\)
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)
+This warning is for project developers\.  Use -Wno-dev to suppress it\.
+
+CMake Error at file-GET_RUNTIME_DEPENDENCIES-badargs2\.cmake:[0-9]+ \(file\):
+  file Keyword missing value: BUNDLE_EXECUTABLE
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs2.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs2.cmake
new file mode 100644
index 0000000..138ab95
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs2.cmake
@@ -0,0 +1,2 @@
+file(GET_RUNTIME_DEPENDENCIES BUNDLE_EXECUTABLE)
+message(FATAL_ERROR "This message should not be displayed")
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-all-check.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-all-check.cmake
new file mode 100644
index 0000000..ab630f0
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-all-check.cmake
@@ -0,0 +1,44 @@
+function(check_contents filename contents_regex)
+  if(EXISTS "${CMAKE_INSTALL_PREFIX}/${filename}")
+    file(READ "${CMAKE_INSTALL_PREFIX}/${filename}" contents)
+    if(NOT contents MATCHES "${contents_regex}")
+      string(APPEND RunCMake_TEST_FAILED "File contents:
+  ${contents}
+do not match what we expected:
+  ${contents_regex}
+in file:
+  ${CMAKE_INSTALL_PREFIX}/${filename}\n")
+      set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE)
+    endif()
+  else()
+    string(APPEND RunCMake_TEST_FAILED "File ${CMAKE_INSTALL_PREFIX}/${filename} does not exist")
+    set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE)
+  endif()
+endfunction()
+
+set(_check
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/libtest_rpath\.so]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/libtest_runpath\.so]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/rpath/librpath\.so]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/rpath_parent/librpath_parent\.so]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/rpath_search/librpath_search\.so]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/runpath/librunpath\.so]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/runpath_search/librunpath_search\.so]]
+  )
+check_contents(deps/deps1.txt "^${_check}$")
+check_contents(deps/deps2.txt "^${_check}$")
+check_contents(deps/deps3.txt "^${_check}$")
+set(_check
+  [[librpath_unresolved\.so]]
+  [[librunpath_parent_unresolved\.so]]
+  [[librunpath_unresolved\.so]]
+  )
+check_contents(deps/udeps1.txt "^${_check}$")
+check_contents(deps/udeps2.txt "^${_check}$")
+check_contents(deps/udeps3.txt "^${_check}$")
+set(_check
+  "^libconflict\\.so:[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/conflict/libconflict\\.so;[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/conflict2/libconflict\\.so\n$"
+  )
+check_contents(deps/cdeps1.txt "${_check}")
+check_contents(deps/cdeps2.txt "${_check}")
+check_contents(deps/cdeps3.txt "${_check}")
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-all-stderr.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-all-stderr.txt
new file mode 100644
index 0000000..123ae48
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-all-stderr.txt
@@ -0,0 +1,119 @@
+^CMake Warning at cmake_install\.cmake:[0-9]+ \(file\):
+  Dependency librpath_search_postexcluded\.so found in search directory:
+
+    [^
+]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/rpath_search_postexcluded
+
+  See file\(GET_RUNTIME_DEPENDENCIES\) documentation for more information\.
+Call Stack \(most recent call first\):
+  cmake_install\.cmake:[0-9]+ \(exec_get_runtime_dependencies\)
+
+*CMake Warning at cmake_install\.cmake:[0-9]+ \(file\):
+  Dependency librpath_search\.so found in search directory:
+
+    [^
+]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/rpath_search
+
+  See file\(GET_RUNTIME_DEPENDENCIES\) documentation for more information\.
+Call Stack \(most recent call first\):
+  cmake_install\.cmake:[0-9]+ \(exec_get_runtime_dependencies\)
+
+*CMake Warning at cmake_install\.cmake:[0-9]+ \(file\):
+  Dependency librunpath_search_postexcluded\.so found in search directory:
+
+    [^
+]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/runpath_search_postexcluded
+
+  See file\(GET_RUNTIME_DEPENDENCIES\) documentation for more information\.
+Call Stack \(most recent call first\):
+  cmake_install\.cmake:[0-9]+ \(exec_get_runtime_dependencies\)
+
+*CMake Warning at cmake_install\.cmake:[0-9]+ \(file\):
+  Dependency librunpath_search\.so found in search directory:
+
+    [^
+]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/runpath_search
+
+  See file\(GET_RUNTIME_DEPENDENCIES\) documentation for more information\.
+Call Stack \(most recent call first\):
+  cmake_install\.cmake:[0-9]+ \(exec_get_runtime_dependencies\)
+
+*CMake Warning at cmake_install\.cmake:[0-9]+ \(file\):
+  Dependency librpath_search_postexcluded\.so found in search directory:
+
+    [^
+]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/rpath_search_postexcluded
+
+  See file\(GET_RUNTIME_DEPENDENCIES\) documentation for more information\.
+Call Stack \(most recent call first\):
+  cmake_install\.cmake:[0-9]+ \(exec_get_runtime_dependencies\)
+
+*CMake Warning at cmake_install\.cmake:[0-9]+ \(file\):
+  Dependency librpath_search\.so found in search directory:
+
+    [^
+]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/rpath_search
+
+  See file\(GET_RUNTIME_DEPENDENCIES\) documentation for more information\.
+Call Stack \(most recent call first\):
+  cmake_install\.cmake:[0-9]+ \(exec_get_runtime_dependencies\)
+
+*CMake Warning at cmake_install\.cmake:[0-9]+ \(file\):
+  Dependency librunpath_search_postexcluded\.so found in search directory:
+
+    [^
+]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/runpath_search_postexcluded
+
+  See file\(GET_RUNTIME_DEPENDENCIES\) documentation for more information\.
+Call Stack \(most recent call first\):
+  cmake_install\.cmake:[0-9]+ \(exec_get_runtime_dependencies\)
+
+*CMake Warning at cmake_install\.cmake:[0-9]+ \(file\):
+  Dependency librunpath_search\.so found in search directory:
+
+    [^
+]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/runpath_search
+
+  See file\(GET_RUNTIME_DEPENDENCIES\) documentation for more information\.
+Call Stack \(most recent call first\):
+  cmake_install\.cmake:[0-9]+ \(exec_get_runtime_dependencies\)
+
+*CMake Warning at cmake_install\.cmake:[0-9]+ \(file\):
+  Dependency librpath_search_postexcluded\.so found in search directory:
+
+    [^
+]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/rpath_search_postexcluded
+
+  See file\(GET_RUNTIME_DEPENDENCIES\) documentation for more information\.
+Call Stack \(most recent call first\):
+  cmake_install\.cmake:[0-9]+ \(exec_get_runtime_dependencies\)
+
+*CMake Warning at cmake_install\.cmake:[0-9]+ \(file\):
+  Dependency librpath_search\.so found in search directory:
+
+    [^
+]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/rpath_search
+
+  See file\(GET_RUNTIME_DEPENDENCIES\) documentation for more information\.
+Call Stack \(most recent call first\):
+  cmake_install\.cmake:[0-9]+ \(exec_get_runtime_dependencies\)
+
+*CMake Warning at cmake_install\.cmake:[0-9]+ \(file\):
+  Dependency librunpath_search_postexcluded\.so found in search directory:
+
+    [^
+]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/runpath_search_postexcluded
+
+  See file\(GET_RUNTIME_DEPENDENCIES\) documentation for more information\.
+Call Stack \(most recent call first\):
+  cmake_install\.cmake:[0-9]+ \(exec_get_runtime_dependencies\)
+
+*CMake Warning at cmake_install\.cmake:[0-9]+ \(file\):
+  Dependency librunpath_search\.so found in search directory:
+
+    [^
+]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/runpath_search
+
+  See file\(GET_RUNTIME_DEPENDENCIES\) documentation for more information\.
+Call Stack \(most recent call first\):
+  cmake_install\.cmake:[0-9]+ \(exec_get_runtime_dependencies\)$
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-conflict-all-result.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-conflict-all-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-conflict-all-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-conflict-all-stderr.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-conflict-all-stderr.txt
new file mode 100644
index 0000000..1692348
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-conflict-all-stderr.txt
@@ -0,0 +1,7 @@
+^CMake Error at cmake_install\.cmake:[0-9]+ \(file\):
+  file Multiple conflicting paths found for librpath\.so:
+
+    [^
+]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-conflict-build/root-all/lib/rpath1/librpath\.so
+    [^
+]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-conflict-build/root-all/lib/rpath2/librpath\.so$
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-conflict.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-conflict.cmake
new file mode 100644
index 0000000..f719499
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-conflict.cmake
@@ -0,0 +1,54 @@
+enable_language(C)
+
+set(test1_names rpath)
+set(test2_names rpath)
+
+file(WRITE "${CMAKE_BINARY_DIR}/rpath.c" "void rpath(void) {}\n")
+add_library(rpath SHARED "${CMAKE_BINARY_DIR}/rpath.c")
+install(TARGETS rpath DESTINATION lib/rpath1)
+install(TARGETS rpath DESTINATION lib/rpath2)
+
+file(REMOVE "${CMAKE_BINARY_DIR}/test1.c")
+add_library(test1 SHARED "${CMAKE_BINARY_DIR}/test1.c")
+foreach(name ${test1_names})
+  file(APPEND "${CMAKE_BINARY_DIR}/test1.c" "extern void ${name}(void);\n")
+endforeach()
+file(APPEND "${CMAKE_BINARY_DIR}/test1.c" "void test1(void)\n{\n")
+foreach(name ${test1_names})
+  file(APPEND "${CMAKE_BINARY_DIR}/test1.c" "  ${name}();\n")
+endforeach()
+file(APPEND "${CMAKE_BINARY_DIR}/test1.c" "}\n")
+
+target_link_libraries(test1 PRIVATE ${test1_names})
+set_property(TARGET test1 PROPERTY INSTALL_RPATH
+  "${CMAKE_BINARY_DIR}/root-all/lib/rpath1"
+  )
+
+file(REMOVE "${CMAKE_BINARY_DIR}/test2.c")
+add_library(test2 SHARED "${CMAKE_BINARY_DIR}/test2.c")
+foreach(name ${test2_names})
+  file(APPEND "${CMAKE_BINARY_DIR}/test2.c" "extern void ${name}(void);\n")
+endforeach()
+file(APPEND "${CMAKE_BINARY_DIR}/test2.c" "void test2(void)\n{\n")
+foreach(name ${test2_names})
+  file(APPEND "${CMAKE_BINARY_DIR}/test2.c" "  ${name}();\n")
+endforeach()
+file(APPEND "${CMAKE_BINARY_DIR}/test2.c" "}\n")
+
+target_link_libraries(test2 PRIVATE ${test2_names})
+set_property(TARGET test2 PROPERTY INSTALL_RPATH
+  "${CMAKE_BINARY_DIR}/root-all/lib/rpath2"
+  )
+
+install(TARGETS test1 test2 DESTINATION lib)
+
+install(CODE [[
+  file(GET_RUNTIME_DEPENDENCIES
+    LIBRARIES
+      "${CMAKE_INSTALL_PREFIX}/lib/$<TARGET_FILE_NAME:test1>"
+      "${CMAKE_INSTALL_PREFIX}/lib/$<TARGET_FILE_NAME:test2>"
+    PRE_INCLUDE_REGEXES "^librpath\\.so$"
+    PRE_EXCLUDE_REGEXES ".*"
+    )
+  message(FATAL_ERROR "This message should not be displayed")
+  ]])
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-notfile-all-result.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-notfile-all-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-notfile-all-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-notfile-all-stderr.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-notfile-all-stderr.txt
new file mode 100644
index 0000000..83a87c9
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-notfile-all-stderr.txt
@@ -0,0 +1,5 @@
+^CMake Error at cmake_install\.cmake:[0-9]+ \(file\):
+  file Failed to run objdump on:
+
+    [^
+]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-notfile-build/root-all/bin/\.\./lib/libtest\.so$
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-notfile.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-notfile.cmake
new file mode 100644
index 0000000..6567438
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-notfile.cmake
@@ -0,0 +1,30 @@
+enable_language(C)
+cmake_policy(SET CMP0095 NEW)
+
+file(WRITE "${CMAKE_BINARY_DIR}/test.c" "void test(void) {}\n")
+file(WRITE "${CMAKE_BINARY_DIR}/main.c" [[extern void test(void);
+
+int main(void)
+{
+  test();
+  return 0;
+}
+]])
+
+add_library(test SHARED "${CMAKE_BINARY_DIR}/test.c")
+add_executable(exe "${CMAKE_BINARY_DIR}/main.c")
+target_link_libraries(exe PRIVATE test)
+set_property(TARGET exe PROPERTY INSTALL_RPATH "\${ORIGIN}/../lib")
+
+install(TARGETS exe DESTINATION bin)
+
+install(CODE [[
+  file(MAKE_DIRECTORY "${CMAKE_INSTALL_PREFIX}/lib/$<TARGET_FILE_NAME:test>")
+  file(GET_RUNTIME_DEPENDENCIES
+    EXECUTABLES
+      "${CMAKE_INSTALL_PREFIX}/bin/$<TARGET_FILE_NAME:exe>"
+    PRE_INCLUDE_REGEXES "^libtest\\.so$"
+    PRE_EXCLUDE_REGEXES ".*"
+    )
+  message(FATAL_ERROR "This message should not be displayed")
+  ]])
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-unresolved-all-result.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-unresolved-all-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-unresolved-all-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-unresolved-all-stderr.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-unresolved-all-stderr.txt
new file mode 100644
index 0000000..eaca512
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-unresolved-all-stderr.txt
@@ -0,0 +1,2 @@
+^CMake Error at cmake_install\.cmake:[0-9]+ \(file\):
+  file Could not resolve file libunresolved\.so$
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-unresolved.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-unresolved.cmake
new file mode 100644
index 0000000..3efa305
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-unresolved.cmake
@@ -0,0 +1,18 @@
+enable_language(C)
+
+file(WRITE "${CMAKE_BINARY_DIR}/testlib.c" "extern void unresolved(void);\nvoid testlib(void)\n{\n  unresolved();\n}\n")
+add_library(testlib SHARED "${CMAKE_BINARY_DIR}/testlib.c")
+file(WRITE "${CMAKE_BINARY_DIR}/unresolved.c" "void unresolved(void) {}\n")
+add_library(unresolved SHARED "${CMAKE_BINARY_DIR}/unresolved.c")
+target_link_libraries(testlib PRIVATE unresolved)
+install(TARGETS testlib DESTINATION lib)
+
+install(CODE [[
+  file(GET_RUNTIME_DEPENDENCIES
+    PRE_INCLUDE_REGEXES "^libunresolved\\.so$"
+    PRE_EXCLUDE_REGEXES ".*"
+    LIBRARIES
+      "${CMAKE_INSTALL_PREFIX}/lib/$<TARGET_FILE_NAME:testlib>"
+    )
+  message(FATAL_ERROR "This message should not be displayed")
+  ]])
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux.cmake
new file mode 100644
index 0000000..bd0f9f1
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux.cmake
@@ -0,0 +1,169 @@
+enable_language(C)
+cmake_policy(SET CMP0095 NEW)
+
+set(test_rpath_names
+  preexcluded
+  rpath_postexcluded
+  rpath
+  rpath_parent_postexcluded
+  rpath_parent
+  rpath_origin_postexcluded
+  rpath_origin
+  rpath_search_postexcluded
+  rpath_search
+  rpath_unresolved
+  conflict
+  )
+set(test_runpath_names
+  runpath_postexcluded
+  runpath
+  runpath_origin_postexcluded
+  runpath_origin
+  runpath_parent_unresolved
+  runpath_search_postexcluded
+  runpath_search
+  runpath_unresolved
+  )
+
+file(REMOVE "${CMAKE_BINARY_DIR}/test_rpath.c")
+add_library(test_rpath SHARED "${CMAKE_BINARY_DIR}/test_rpath.c")
+foreach(name ${test_rpath_names})
+  file(WRITE "${CMAKE_BINARY_DIR}/${name}.c" "void ${name}(void) {}\n")
+  add_library(${name} SHARED "${CMAKE_BINARY_DIR}/${name}.c")
+
+  file(APPEND "${CMAKE_BINARY_DIR}/test_rpath.c" "extern void ${name}(void);\n")
+endforeach()
+file(APPEND "${CMAKE_BINARY_DIR}/test_rpath.c" "void test_rpath(void)\n{\n")
+foreach(name ${test_rpath_names})
+  file(APPEND "${CMAKE_BINARY_DIR}/test_rpath.c" "  ${name}();\n")
+endforeach()
+file(APPEND "${CMAKE_BINARY_DIR}/test_rpath.c" "}\n")
+
+install(TARGETS rpath_postexcluded DESTINATION lib/rpath_postexcluded)
+install(TARGETS rpath DESTINATION lib/rpath)
+install(TARGETS rpath_origin_postexcluded DESTINATION lib/rpath_origin_postexcluded)
+install(TARGETS rpath_origin DESTINATION lib/rpath_origin)
+install(TARGETS rpath_parent_postexcluded DESTINATION lib/rpath_parent_postexcluded)
+install(TARGETS rpath rpath_origin rpath_parent DESTINATION lib/rpath_parent)
+install(TARGETS rpath_search_postexcluded DESTINATION lib/rpath_search_postexcluded)
+install(TARGETS rpath rpath_origin rpath_parent rpath_search DESTINATION lib/rpath_search)
+install(TARGETS conflict DESTINATION lib/conflict)
+
+target_link_libraries(test_rpath PRIVATE ${test_rpath_names})
+set_property(TARGET test_rpath PROPERTY INSTALL_RPATH
+  "${CMAKE_BINARY_DIR}/root-all/lib/rpath_postexcluded"
+  "${CMAKE_BINARY_DIR}/root-all/lib/rpath"
+  "\$ORIGIN/rpath_origin_postexcluded"
+  "\${ORIGIN}/rpath_origin"
+  "${CMAKE_BINARY_DIR}/root-all/lib/conflict"
+  )
+target_link_options(test_rpath PRIVATE -Wl,--disable-new-dtags)
+
+file(REMOVE "${CMAKE_BINARY_DIR}/test_runpath.c")
+add_library(test_runpath SHARED "${CMAKE_BINARY_DIR}/test_runpath.c")
+foreach(name ${test_runpath_names} rpath conflict)
+  file(WRITE "${CMAKE_BINARY_DIR}/${name}.c" "void ${name}(void) {}\n")
+  if(NOT name MATCHES "^(rpath|conflict)$")
+    add_library(${name} SHARED "${CMAKE_BINARY_DIR}/${name}.c")
+  endif()
+
+  file(APPEND "${CMAKE_BINARY_DIR}/test_runpath.c" "extern void ${name}(void);\n")
+endforeach()
+file(APPEND "${CMAKE_BINARY_DIR}/test_runpath.c" "void test_runpath(void)\n{\n")
+foreach(name ${test_runpath_names} rpath conflict)
+  file(APPEND "${CMAKE_BINARY_DIR}/test_runpath.c" "  ${name}();\n")
+endforeach()
+file(APPEND "${CMAKE_BINARY_DIR}/test_runpath.c" "}\n")
+
+install(TARGETS runpath_postexcluded DESTINATION lib/runpath_postexcluded)
+install(TARGETS runpath DESTINATION lib/runpath)
+install(TARGETS runpath_origin_postexcluded DESTINATION lib/runpath_origin_postexcluded)
+install(TARGETS runpath_origin DESTINATION lib/runpath_origin)
+install(TARGETS runpath_parent_unresolved DESTINATION lib/runpath_parent_unresolved)
+install(TARGETS runpath_search_postexcluded DESTINATION lib/runpath_search_postexcluded)
+install(TARGETS runpath runpath_origin runpath_search DESTINATION lib/runpath_search)
+install(TARGETS conflict DESTINATION lib/conflict2)
+
+target_link_libraries(test_runpath PRIVATE ${test_runpath_names} rpath conflict)
+set_property(TARGET test_runpath PROPERTY INSTALL_RPATH
+  "${CMAKE_BINARY_DIR}/root-all/lib/runpath/../rpath" # Ensure that files that don't conflict are treated correctly
+  "${CMAKE_BINARY_DIR}/root-all/lib/runpath_postexcluded"
+  "${CMAKE_BINARY_DIR}/root-all/lib/runpath"
+  "\${ORIGIN}/runpath_origin_postexcluded"
+  "\$ORIGIN/runpath_origin"
+  "${CMAKE_BINARY_DIR}/root-all/lib/conflict2"
+  )
+target_link_options(test_runpath PRIVATE -Wl,--enable-new-dtags)
+
+set_property(TARGET test_rpath ${test_rpath_names} test_runpath ${test_runpath_names} PROPERTY LIBRARY_OUTPUT_DIRECTORY lib)
+install(TARGETS test_rpath test_runpath DESTINATION lib)
+
+add_executable(topexe file-GET_RUNTIME_DEPENDENCIES-linux/topexe.c)
+add_library(toplib SHARED file-GET_RUNTIME_DEPENDENCIES-linux/toplib.c)
+add_library(topmod MODULE file-GET_RUNTIME_DEPENDENCIES-linux/toplib.c)
+target_link_libraries(topexe PRIVATE test_rpath test_runpath)
+target_link_libraries(toplib PRIVATE test_rpath test_runpath)
+target_link_libraries(topmod PRIVATE test_rpath test_runpath)
+set_property(TARGET topexe toplib topmod PROPERTY INSTALL_RPATH
+  "${CMAKE_BINARY_DIR}/root-all/lib"
+  "${CMAKE_BINARY_DIR}/root-all/lib/rpath_parent_postexcluded"
+  "${CMAKE_BINARY_DIR}/root-all/lib/rpath_parent"
+  "${CMAKE_BINARY_DIR}/root-all/lib/runpath_parent_unresolved"
+  )
+target_link_options(topexe PRIVATE -Wl,--disable-new-dtags)
+target_link_options(toplib PRIVATE -Wl,--disable-new-dtags)
+target_link_options(topmod PRIVATE -Wl,--disable-new-dtags)
+
+install(TARGETS topexe toplib RUNTIME DESTINATION bin LIBRARY DESTINATION lib)
+install(TARGETS topmod LIBRARY DESTINATION lib/modules)
+
+install(CODE [[
+  function(exec_get_runtime_dependencies depsfile udepsfile cdepsfile)
+    file(GET_RUNTIME_DEPENDENCIES
+      RESOLVED_DEPENDENCIES_VAR deps
+      UNRESOLVED_DEPENDENCIES_VAR udeps
+      CONFLICTING_DEPENDENCIES_PREFIX cdeps
+      PRE_INCLUDE_REGEXES
+        "^lib(test_rpath|rpath_postexcluded|rpath|rpath_parent_postexcluded|rpath_parent|rpath_origin_postexcluded|rpath_origin|rpath_search_postexcluded|rpath_search|rpath_unresolved|test_runpath|runpath_postexcluded|runpath|runpath_origin_postexcluded|runpath_origin|runpath_parent_unresolved|runpath_search_postexcluded|runpath_search|runpath_unresolved|conflict)\\.so$"
+        "^libc\\.so"
+      PRE_EXCLUDE_REGEXES ".*"
+      POST_INCLUDE_REGEXES "^.*/(libtest_rpath|rpath/librpath|rpath_parent/librpath_parent|rpath_search/librpath_search|libtest_runpath|runpath/librunpath|runpath_origin_postexcluded|runpath_origin|runpath_search/librunpath_search|conflict2?/libconflict)\\.so$"
+      POST_EXCLUDE_REGEXES ".*"
+      DIRECTORIES
+        "${CMAKE_INSTALL_PREFIX}/lib/rpath_search_postexcluded"
+        "${CMAKE_INSTALL_PREFIX}/lib/rpath_search"
+        "${CMAKE_INSTALL_PREFIX}/lib/runpath_search_postexcluded"
+        "${CMAKE_INSTALL_PREFIX}/lib/runpath_search"
+      ${ARGN}
+      )
+    list(SORT deps)
+    list(SORT udeps)
+    list(SORT cdeps_FILENAMES)
+    file(WRITE "${CMAKE_INSTALL_PREFIX}/deps/${depsfile}" "${deps}")
+    file(WRITE "${CMAKE_INSTALL_PREFIX}/deps/${udepsfile}" "${udeps}")
+    file(WRITE "${CMAKE_INSTALL_PREFIX}/deps/${cdepsfile}" "")
+    foreach(cdep IN LISTS cdeps_FILENAMES)
+      set(cdep_values ${cdeps_${cdep}})
+      list(SORT cdep_values)
+      file(APPEND "${CMAKE_INSTALL_PREFIX}/deps/${cdepsfile}" "${cdep}:${cdep_values}\n")
+    endforeach()
+  endfunction()
+
+  exec_get_runtime_dependencies(
+    deps1.txt udeps1.txt cdeps1.txt
+    EXECUTABLES
+      "${CMAKE_INSTALL_PREFIX}/bin/$<TARGET_FILE_NAME:topexe>"
+    )
+
+  exec_get_runtime_dependencies(
+    deps2.txt udeps2.txt cdeps2.txt
+    LIBRARIES
+      "${CMAKE_INSTALL_PREFIX}/lib/$<TARGET_FILE_NAME:toplib>"
+    )
+
+  exec_get_runtime_dependencies(
+    deps3.txt udeps3.txt cdeps3.txt
+    MODULES
+      "${CMAKE_INSTALL_PREFIX}/lib/modules/$<TARGET_FILE_NAME:topmod>"
+    )
+  ]])
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux/topexe.c b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux/topexe.c
new file mode 100644
index 0000000..d196afe
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux/topexe.c
@@ -0,0 +1,9 @@
+extern void test_rpath(void);
+extern void test_runpath(void);
+
+int main(void)
+{
+  test_rpath();
+  test_runpath();
+  return 0;
+}
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux/toplib.c b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux/toplib.c
new file mode 100644
index 0000000..040e591
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux/toplib.c
@@ -0,0 +1,8 @@
+extern void test_rpath(void);
+extern void test_runpath(void);
+
+void toplib(void)
+{
+  test_rpath();
+  test_runpath();
+}
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-all-check.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-all-check.cmake
new file mode 100644
index 0000000..4d6dde1
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-all-check.cmake
@@ -0,0 +1,157 @@
+function(check_contents filename contents_regex)
+  if(EXISTS "${CMAKE_INSTALL_PREFIX}/${filename}")
+    file(READ "${CMAKE_INSTALL_PREFIX}/${filename}" contents)
+    if(NOT contents MATCHES "${contents_regex}")
+      string(APPEND RunCMake_TEST_FAILED "File contents:
+  ${contents}
+do not match what we expected:
+  ${contents_regex}
+in file:
+  ${CMAKE_INSTALL_PREFIX}/${filename}\n")
+      set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE)
+    endif()
+  else()
+    string(APPEND RunCMake_TEST_FAILED "File ${CMAKE_INSTALL_PREFIX}/${filename} does not exist")
+    set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE)
+  endif()
+endfunction()
+
+set(_check
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/bin/../lib/executable_path/libexecutable_path\.dylib]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/bin/../lib/rpath_executable_path/librpath_executable_path\.dylib]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/libtestlib\.dylib]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/loader_path/libloader_path\.dylib]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/normal/../rpath/librpath\.dylib]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/normal/libnormal\.dylib]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/rpath_loader_path/librpath_loader_path\.dylib]]
+  [[/usr/lib/libSystem\.B\.dylib]]
+  )
+check_contents(deps/deps1.txt "^${_check}$")
+
+set(_check
+  [[@executable_path/../lib/executable_path_bundle/libexecutable_path_bundle\.dylib]]
+  [[@loader_path/loader_path_unresolved/libloader_path_unresolved\.dylib]]
+  [[@rpath/librpath_executable_path_bundle\.dylib]]
+  [[@rpath/librpath_loader_path_unresolved\.dylib]]
+  [[@rpath/librpath_unresolved\.dylib]]
+  )
+check_contents(deps/udeps1.txt "^${_check}$")
+
+set(_check
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/libtestlib\.dylib]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/loader_path/libloader_path\.dylib]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/normal/../rpath/librpath\.dylib]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/normal/libnormal\.dylib]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/rpath_loader_path/librpath_loader_path\.dylib]]
+  [[/usr/lib/libSystem\.B\.dylib]]
+  )
+check_contents(deps/deps2.txt "^${_check}$")
+
+set(_check
+  [[@executable_path/../lib/executable_path/libexecutable_path\.dylib]]
+  [[@executable_path/../lib/executable_path_bundle/libexecutable_path_bundle\.dylib]]
+  [[@executable_path/../lib/executable_path_postexcluded/libexecutable_path_postexcluded\.dylib]]
+  [[@loader_path/loader_path_unresolved/libloader_path_unresolved\.dylib]]
+  [[@rpath/librpath_executable_path\.dylib]]
+  [[@rpath/librpath_executable_path_bundle\.dylib]]
+  [[@rpath/librpath_executable_path_postexcluded\.dylib]]
+  [[@rpath/librpath_loader_path_unresolved\.dylib]]
+  [[@rpath/librpath_unresolved\.dylib]]
+  )
+check_contents(deps/udeps2.txt "^${_check}$")
+
+set(_check
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/libtestlib\.dylib]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/loader_path/libloader_path\.dylib]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/normal/../rpath/librpath\.dylib]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/normal/libnormal\.dylib]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/rpath_loader_path/librpath_loader_path\.dylib]]
+  [[/usr/lib/libSystem\.B\.dylib]]
+  )
+check_contents(deps/deps3.txt "^${_check}$")
+
+set(_check
+  [[@executable_path/../lib/executable_path/libexecutable_path\.dylib]]
+  [[@executable_path/../lib/executable_path_bundle/libexecutable_path_bundle\.dylib]]
+  [[@executable_path/../lib/executable_path_postexcluded/libexecutable_path_postexcluded\.dylib]]
+  [[@loader_path/loader_path_unresolved/libloader_path_unresolved\.dylib]]
+  [[@rpath/librpath_executable_path\.dylib]]
+  [[@rpath/librpath_executable_path_bundle\.dylib]]
+  [[@rpath/librpath_executable_path_postexcluded\.dylib]]
+  [[@rpath/librpath_loader_path_unresolved\.dylib]]
+  [[@rpath/librpath_unresolved\.dylib]]
+  )
+check_contents(deps/udeps3.txt "^${_check}$")
+
+set(_check
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/bin/../lib/executable_path/libexecutable_path\.dylib]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/bin/../lib/rpath_executable_path/librpath_executable_path\.dylib]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/libtestlib\.dylib]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/loader_path/libloader_path\.dylib]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/normal/../rpath/librpath\.dylib]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/normal/libnormal\.dylib]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/rpath_loader_path/librpath_loader_path\.dylib]]
+  [[/usr/lib/libSystem\.B\.dylib]]
+  )
+check_contents(deps/deps4.txt "^${_check}$")
+
+set(_check
+  [[@executable_path/../lib/executable_path_bundle/libexecutable_path_bundle\.dylib]]
+  [[@loader_path/loader_path_unresolved/libloader_path_unresolved\.dylib]]
+  [[@rpath/librpath_executable_path_bundle\.dylib]]
+  [[@rpath/librpath_loader_path_unresolved\.dylib]]
+  [[@rpath/librpath_unresolved\.dylib]]
+  )
+check_contents(deps/udeps4.txt "^${_check}$")
+
+set(_check
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/bundle_executable/bin/../lib/executable_path_bundle/libexecutable_path_bundle\.dylib]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/libtestlib\.dylib]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/loader_path/libloader_path\.dylib]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/normal/../rpath/librpath\.dylib]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/normal/libnormal\.dylib]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/rpath_loader_path/librpath_loader_path\.dylib]]
+  [[/usr/lib/libSystem\.B\.dylib]]
+  )
+check_contents(deps/deps5.txt "^${_check}$")
+
+set(_check
+  [[@executable_path/../lib/executable_path/libexecutable_path\.dylib]]
+  [[@loader_path/loader_path_unresolved/libloader_path_unresolved\.dylib]]
+  [[@rpath/librpath_executable_path\.dylib]]
+  [[@rpath/librpath_executable_path_bundle\.dylib]]
+  [[@rpath/librpath_loader_path_unresolved\.dylib]]
+  [[@rpath/librpath_unresolved\.dylib]]
+  )
+check_contents(deps/udeps5.txt "^${_check}$")
+
+set(_check
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/bundle_executable/bin/../lib/executable_path_bundle/libexecutable_path_bundle\.dylib]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/libtestlib\.dylib]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/loader_path/libloader_path\.dylib]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/normal/../rpath/librpath\.dylib]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/normal/libnormal\.dylib]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/rpath_loader_path/librpath_loader_path\.dylib]]
+  [[/usr/lib/libSystem\.B\.dylib]]
+  )
+check_contents(deps/deps6.txt "^${_check}$")
+
+set(_check
+  [[@executable_path/../lib/executable_path/libexecutable_path\.dylib]]
+  [[@loader_path/loader_path_unresolved/libloader_path_unresolved\.dylib]]
+  [[@rpath/librpath_executable_path\.dylib]]
+  [[@rpath/librpath_executable_path_bundle\.dylib]]
+  [[@rpath/librpath_loader_path_unresolved\.dylib]]
+  [[@rpath/librpath_unresolved\.dylib]]
+  )
+check_contents(deps/udeps6.txt "^${_check}$")
+
+set(_check
+  "^libconflict\\.dylib:[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/conflict/libconflict\\.dylib;[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/conflict2/libconflict\\.dylib\n$"
+  )
+check_contents(deps/cdeps1.txt "${_check}")
+check_contents(deps/cdeps2.txt "${_check}")
+check_contents(deps/cdeps3.txt "${_check}")
+check_contents(deps/cdeps4.txt "${_check}")
+check_contents(deps/cdeps5.txt "${_check}")
+check_contents(deps/cdeps6.txt "${_check}")
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-conflict-all-result.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-conflict-all-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-conflict-all-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-conflict-all-stderr.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-conflict-all-stderr.txt
new file mode 100644
index 0000000..bc9e97a
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-conflict-all-stderr.txt
@@ -0,0 +1,7 @@
+^CMake Error at cmake_install\.cmake:[0-9]+ \(file\):
+  file Multiple conflicting paths found for librpath\.dylib:
+
+    [^
+]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-conflict-build/root-all/lib/rpath1/librpath\.dylib
+    [^
+]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-conflict-build/root-all/lib/rpath2/librpath\.dylib$
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-conflict.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-conflict.cmake
new file mode 100644
index 0000000..a8446fe
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-conflict.cmake
@@ -0,0 +1,55 @@
+enable_language(C)
+
+set(test1_names rpath)
+set(test2_names rpath)
+
+file(WRITE "${CMAKE_BINARY_DIR}/rpath.c" "void rpath(void) {}\n")
+add_library(rpath SHARED "${CMAKE_BINARY_DIR}/rpath.c")
+set_property(TARGET rpath PROPERTY INSTALL_NAME_DIR @rpath)
+install(TARGETS rpath DESTINATION lib/rpath1)
+install(TARGETS rpath DESTINATION lib/rpath2)
+
+file(REMOVE "${CMAKE_BINARY_DIR}/test1.c")
+add_library(test1 SHARED "${CMAKE_BINARY_DIR}/test1.c")
+foreach(name ${test1_names})
+  file(APPEND "${CMAKE_BINARY_DIR}/test1.c" "extern void ${name}(void);\n")
+endforeach()
+file(APPEND "${CMAKE_BINARY_DIR}/test1.c" "void test1(void)\n{\n")
+foreach(name ${test1_names})
+  file(APPEND "${CMAKE_BINARY_DIR}/test1.c" "  ${name}();\n")
+endforeach()
+file(APPEND "${CMAKE_BINARY_DIR}/test1.c" "}\n")
+
+target_link_libraries(test1 PRIVATE ${test1_names})
+set_property(TARGET test1 PROPERTY INSTALL_RPATH
+  "${CMAKE_BINARY_DIR}/root-all/lib/rpath1"
+  )
+
+file(REMOVE "${CMAKE_BINARY_DIR}/test2.c")
+add_library(test2 SHARED "${CMAKE_BINARY_DIR}/test2.c")
+foreach(name ${test2_names})
+  file(APPEND "${CMAKE_BINARY_DIR}/test2.c" "extern void ${name}(void);\n")
+endforeach()
+file(APPEND "${CMAKE_BINARY_DIR}/test2.c" "void test2(void)\n{\n")
+foreach(name ${test2_names})
+  file(APPEND "${CMAKE_BINARY_DIR}/test2.c" "  ${name}();\n")
+endforeach()
+file(APPEND "${CMAKE_BINARY_DIR}/test2.c" "}\n")
+
+target_link_libraries(test2 PRIVATE ${test2_names})
+set_property(TARGET test2 PROPERTY INSTALL_RPATH
+  "${CMAKE_BINARY_DIR}/root-all/lib/rpath2"
+  )
+
+install(TARGETS test1 test2 DESTINATION lib)
+
+install(CODE [[
+  file(GET_RUNTIME_DEPENDENCIES
+    LIBRARIES
+      "${CMAKE_INSTALL_PREFIX}/lib/$<TARGET_FILE_NAME:test1>"
+      "${CMAKE_INSTALL_PREFIX}/lib/$<TARGET_FILE_NAME:test2>"
+    PRE_INCLUDE_REGEXES "^@rpath/librpath\\.dylib$"
+    PRE_EXCLUDE_REGEXES ".*"
+    )
+  message(FATAL_ERROR "This message should not be displayed")
+  ]])
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-notfile-all-result.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-notfile-all-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-notfile-all-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-notfile-all-stderr.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-notfile-all-stderr.txt
new file mode 100644
index 0000000..73ab9f1
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-notfile-all-stderr.txt
@@ -0,0 +1,5 @@
+^CMake Error at cmake_install\.cmake:[0-9]+ \(file\):
+  file Failed to run otool on:
+
+    [^
+]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-notfile-build/root-all/bin/\.\./lib/libtest\.dylib$
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-notfile.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-notfile.cmake
new file mode 100644
index 0000000..3e4c434
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-notfile.cmake
@@ -0,0 +1,30 @@
+enable_language(C)
+
+file(WRITE "${CMAKE_BINARY_DIR}/test.c" "void test(void) {}\n")
+file(WRITE "${CMAKE_BINARY_DIR}/main.c" [[extern void test(void);
+
+int main(void)
+{
+  test();
+  return 0;
+}
+]])
+
+add_library(test SHARED "${CMAKE_BINARY_DIR}/test.c")
+set_property(TARGET test PROPERTY INSTALL_NAME_DIR @rpath)
+add_executable(exe "${CMAKE_BINARY_DIR}/main.c")
+target_link_libraries(exe PRIVATE test)
+set_property(TARGET exe PROPERTY INSTALL_RPATH "@loader_path/../lib")
+
+install(TARGETS exe DESTINATION bin)
+
+install(CODE [[
+  file(MAKE_DIRECTORY "${CMAKE_INSTALL_PREFIX}/lib/$<TARGET_FILE_NAME:test>")
+  file(GET_RUNTIME_DEPENDENCIES
+    EXECUTABLES
+      "${CMAKE_INSTALL_PREFIX}/bin/$<TARGET_FILE_NAME:exe>"
+    PRE_INCLUDE_REGEXES "^@rpath/libtest\\.dylib$"
+    PRE_EXCLUDE_REGEXES ".*"
+    )
+  message(FATAL_ERROR "This message should not be displayed")
+  ]])
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-unresolved-all-result.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-unresolved-all-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-unresolved-all-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-unresolved-all-stderr.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-unresolved-all-stderr.txt
new file mode 100644
index 0000000..01762b4
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-unresolved-all-stderr.txt
@@ -0,0 +1,2 @@
+^CMake Error at cmake_install\.cmake:[0-9]+ \(file\):
+  file Could not resolve file @rpath/libunresolved\.dylib$
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-unresolved.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-unresolved.cmake
new file mode 100644
index 0000000..c9b6c95
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-unresolved.cmake
@@ -0,0 +1,18 @@
+enable_language(C)
+
+file(WRITE "${CMAKE_BINARY_DIR}/testlib.c" "extern void unresolved(void);\nvoid testlib(void)\n{\n  unresolved();\n}\n")
+add_library(testlib SHARED "${CMAKE_BINARY_DIR}/testlib.c")
+file(WRITE "${CMAKE_BINARY_DIR}/unresolved.c" "void unresolved(void) {}\n")
+add_library(unresolved SHARED "${CMAKE_BINARY_DIR}/unresolved.c")
+target_link_libraries(testlib PRIVATE unresolved)
+install(TARGETS testlib DESTINATION lib)
+
+install(CODE [[
+  file(GET_RUNTIME_DEPENDENCIES
+    PRE_INCLUDE_REGEXES "^@rpath/libunresolved\\.dylib$"
+    PRE_EXCLUDE_REGEXES ".*"
+    LIBRARIES
+      "${CMAKE_INSTALL_PREFIX}/lib/$<TARGET_FILE_NAME:testlib>"
+    )
+  message(FATAL_ERROR "This message should not be displayed")
+  ]])
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos.cmake
new file mode 100644
index 0000000..6db05b3
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos.cmake
@@ -0,0 +1,216 @@
+enable_language(C)
+
+set(testlib_names
+  preexcluded
+  executable_path
+  executable_path_bundle
+  executable_path_postexcluded
+  loader_path
+  loader_path_unresolved
+  loader_path_postexcluded
+  rpath
+  rpath_unresolved
+  rpath_postexcluded
+  rpath_executable_path
+  rpath_executable_path_bundle
+  rpath_executable_path_postexcluded
+  rpath_loader_path
+  rpath_loader_path_unresolved
+  rpath_loader_path_postexcluded
+  normal
+  normal_unresolved
+  normal_postexcluded
+  conflict
+  )
+
+file(REMOVE "${CMAKE_BINARY_DIR}/testlib.c")
+add_library(testlib SHARED "${CMAKE_BINARY_DIR}/testlib.c")
+foreach(name ${testlib_names})
+  if(name STREQUAL "normal")
+    file(WRITE "${CMAKE_BINARY_DIR}/normal.c" "extern void rpath(void);\nvoid normal(void)\n{\n  rpath();\n}\n")
+  else()
+    file(WRITE "${CMAKE_BINARY_DIR}/${name}.c" "void ${name}(void) {}\n")
+  endif()
+  add_library(${name} SHARED "${CMAKE_BINARY_DIR}/${name}.c")
+
+  file(APPEND "${CMAKE_BINARY_DIR}/testlib.c" "extern void ${name}(void);\n")
+endforeach()
+file(APPEND "${CMAKE_BINARY_DIR}/testlib.c" "void testlib(void)\n{\n")
+foreach(name ${testlib_names})
+  file(APPEND "${CMAKE_BINARY_DIR}/testlib.c" "  ${name}();\n")
+endforeach()
+file(APPEND "${CMAKE_BINARY_DIR}/testlib.c" "}\n")
+set_property(TARGET ${testlib_names} PROPERTY BUILD_WITH_INSTALL_NAME_DIR 1)
+target_link_libraries(normal PRIVATE rpath)
+set_property(TARGET normal PROPERTY INSTALL_RPATH
+  "${CMAKE_BINARY_DIR}/root-all/executable/lib/normal/../rpath"
+  )
+
+file(WRITE "${CMAKE_BINARY_DIR}/testlib_conflict.c" "extern void conflict(void);\nvoid testlib_conflict(void)\n{\n  conflict();\n}\n")
+add_library(testlib_conflict SHARED "${CMAKE_BINARY_DIR}/testlib_conflict.c")
+target_link_libraries(testlib_conflict PRIVATE conflict)
+
+set_property(TARGET testlib PROPERTY INSTALL_RPATH
+  "${CMAKE_BINARY_DIR}/root-all/executable/lib/rpath"
+  "${CMAKE_BINARY_DIR}/root-all/executable/lib/rpath_unresolved"
+  "${CMAKE_BINARY_DIR}/root-all/executable/lib/rpath_postexcluded"
+  "${CMAKE_BINARY_DIR}/root-all/executable/lib/conflict"
+  @executable_path/../lib/rpath_executable_path
+  @executable_path/../lib/rpath_executable_path_unresolved
+  @executable_path/../lib/rpath_executable_path_postexcluded
+  @loader_path/rpath_loader_path
+  @loader_path/rpath_loader_path_unresolved
+  @loader_path/rpath_loader_path_postexcluded
+  )
+set_property(TARGET testlib_conflict PROPERTY INSTALL_RPATH
+  "${CMAKE_BINARY_DIR}/root-all/executable/lib/conflict2"
+  )
+
+foreach(t
+  executable_path
+  executable_path_postexcluded
+  loader_path
+  loader_path_postexcluded
+  rpath
+  rpath_postexcluded
+  rpath_executable_path
+  rpath_executable_path_postexcluded
+  rpath_loader_path
+  rpath_loader_path_postexcluded
+  conflict
+  )
+  install(TARGETS ${t} DESTINATION executable/lib/${t})
+endforeach()
+install(TARGETS conflict DESTINATION executable/lib/conflict2)
+
+foreach(t
+  executable_path_bundle
+  executable_path_postexcluded
+  loader_path_postexcluded
+  rpath_postexcluded
+  rpath_executable_path_bundle
+  rpath_executable_path_postexcluded
+  rpath_loader_path_postexcluded
+  )
+  install(TARGETS ${t} DESTINATION bundle_executable/lib/${t})
+endforeach()
+
+foreach(t executable_path executable_path_bundle executable_path_postexcluded)
+  set_property(TARGET ${t} PROPERTY INSTALL_NAME_DIR @executable_path/../lib/${t})
+endforeach()
+
+foreach(t loader_path loader_path_unresolved loader_path_postexcluded)
+  set_property(TARGET ${t} PROPERTY INSTALL_NAME_DIR @loader_path/${t})
+endforeach()
+
+foreach(t
+  rpath
+  rpath_unresolved
+  rpath_postexcluded
+  rpath_executable_path
+  rpath_executable_path_bundle
+  rpath_executable_path_postexcluded
+  rpath_loader_path
+  rpath_loader_path_unresolved
+  rpath_loader_path_postexcluded
+  conflict
+  )
+  set_property(TARGET ${t} PROPERTY INSTALL_NAME_DIR @rpath)
+endforeach()
+
+foreach(t normal normal_unresolved normal_postexcluded)
+  set_property(TARGET ${t} PROPERTY INSTALL_NAME_DIR "${CMAKE_BINARY_DIR}/root-all/executable/lib/${t}")
+  if(NOT t STREQUAL "normal_unresolved")
+    install(TARGETS ${t} DESTINATION executable/lib/${t})
+  endif()
+endforeach()
+
+target_link_libraries(testlib PRIVATE ${testlib_names})
+
+add_executable(topexe file-GET_RUNTIME_DEPENDENCIES-macos/topexe.c)
+add_library(toplib SHARED file-GET_RUNTIME_DEPENDENCIES-macos/toplib.c)
+add_library(topmod MODULE file-GET_RUNTIME_DEPENDENCIES-macos/toplib.c)
+target_link_libraries(topexe PRIVATE testlib)
+target_link_libraries(toplib PRIVATE testlib)
+target_link_libraries(topmod PRIVATE testlib)
+
+set_property(TARGET topexe toplib topmod PROPERTY INSTALL_RPATH "${CMAKE_BINARY_DIR}/root-all/executable/lib")
+
+install(TARGETS topexe toplib topmod testlib testlib_conflict RUNTIME DESTINATION executable/bin LIBRARY DESTINATION executable/lib)
+install(TARGETS topexe toplib topmod testlib testlib_conflict RUNTIME DESTINATION bundle_executable/bin LIBRARY DESTINATION bundle_executable/lib)
+
+install(CODE [[
+  function(exec_get_runtime_dependencies depsfile udepsfile cdepsfile)
+    file(GET_RUNTIME_DEPENDENCIES
+      RESOLVED_DEPENDENCIES_VAR deps
+      UNRESOLVED_DEPENDENCIES_VAR udeps
+      CONFLICTING_DEPENDENCIES_PREFIX cdeps
+      PRE_INCLUDE_REGEXES "^.*/lib(testlib|executable_path|executable_path_bundle|executable_path_postexcluded|loader_path|loader_path_unresolved|loader_path_postexcluded|rpath|rpath_unresolved|rpath_postexcluded|rpath_executable_path|rpath_executable_path_bundle|rpath_executable_path_postexcluded|rpath_loader_path|rpath_loader_path_unresolved|rpath_loader_path_postexcluded|normal|normal_unresolved|normal_postexcluded|conflict|System\\.B)\\.dylib$"
+      PRE_EXCLUDE_REGEXES ".*"
+      POST_INCLUDE_REGEXES "^.*/lib(testlib|executable_path|executable_path_bundle|loader_path|rpath|rpath_executable_path|rpath_executable_path_bundle|rpath_loader_path|normal|conflict|System\\.B)\\.dylib$"
+      POST_EXCLUDE_REGEXES ".*"
+      ${ARGN}
+      )
+    list(SORT deps)
+    list(SORT udeps)
+    list(SORT cdeps_FILENAMES)
+    file(WRITE "${CMAKE_INSTALL_PREFIX}/deps/${depsfile}" "${deps}")
+    file(WRITE "${CMAKE_INSTALL_PREFIX}/deps/${udepsfile}" "${udeps}")
+    file(WRITE "${CMAKE_INSTALL_PREFIX}/deps/${cdepsfile}" "")
+    foreach(cdep IN LISTS cdeps_FILENAMES)
+      set(cdep_values ${cdeps_${cdep}})
+      list(SORT cdep_values)
+      file(APPEND "${CMAKE_INSTALL_PREFIX}/deps/${cdepsfile}" "${cdep}:${cdep_values}\n")
+    endforeach()
+  endfunction()
+
+  exec_get_runtime_dependencies(
+    deps1.txt udeps1.txt cdeps1.txt
+    EXECUTABLES
+      "${CMAKE_INSTALL_PREFIX}/executable/bin/$<TARGET_FILE_NAME:topexe>"
+    LIBRARIES
+      "${CMAKE_INSTALL_PREFIX}/executable/lib/$<TARGET_FILE_NAME:testlib_conflict>"
+    )
+
+  exec_get_runtime_dependencies(
+    deps2.txt udeps2.txt cdeps2.txt
+    LIBRARIES
+      "${CMAKE_INSTALL_PREFIX}/executable/lib/$<TARGET_FILE_NAME:toplib>"
+      "${CMAKE_INSTALL_PREFIX}/executable/lib/$<TARGET_FILE_NAME:testlib_conflict>"
+    )
+
+  exec_get_runtime_dependencies(
+    deps3.txt udeps3.txt cdeps3.txt
+    MODULES
+      "${CMAKE_INSTALL_PREFIX}/executable/lib/$<TARGET_FILE_NAME:topmod>"
+    LIBRARIES
+      "${CMAKE_INSTALL_PREFIX}/executable/lib/$<TARGET_FILE_NAME:testlib_conflict>"
+    )
+
+  exec_get_runtime_dependencies(
+    deps4.txt udeps4.txt cdeps4.txt
+    EXECUTABLES
+      "${CMAKE_INSTALL_PREFIX}/executable/bin/$<TARGET_FILE_NAME:topexe>"
+    LIBRARIES
+      "${CMAKE_INSTALL_PREFIX}/executable/lib/$<TARGET_FILE_NAME:testlib_conflict>"
+    BUNDLE_EXECUTABLE
+      "${CMAKE_INSTALL_PREFIX}/bundle_executable/bin/$<TARGET_FILE_NAME:topexe>"
+    )
+
+  exec_get_runtime_dependencies(
+    deps5.txt udeps5.txt cdeps5.txt
+    LIBRARIES
+      "${CMAKE_INSTALL_PREFIX}/executable/lib/$<TARGET_FILE_NAME:toplib>"
+      "${CMAKE_INSTALL_PREFIX}/executable/lib/$<TARGET_FILE_NAME:testlib_conflict>"
+    BUNDLE_EXECUTABLE "${CMAKE_INSTALL_PREFIX}/bundle_executable/bin/$<TARGET_FILE_NAME:topexe>"
+    )
+
+  exec_get_runtime_dependencies(
+    deps6.txt udeps6.txt cdeps6.txt
+    MODULES
+      "${CMAKE_INSTALL_PREFIX}/executable/lib/$<TARGET_FILE_NAME:topmod>"
+    LIBRARIES
+      "${CMAKE_INSTALL_PREFIX}/executable/lib/$<TARGET_FILE_NAME:testlib_conflict>"
+    BUNDLE_EXECUTABLE "${CMAKE_INSTALL_PREFIX}/bundle_executable/bin/$<TARGET_FILE_NAME:topexe>"
+    )
+  ]])
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos/topexe.c b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos/topexe.c
new file mode 100644
index 0000000..20c6087
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos/topexe.c
@@ -0,0 +1,7 @@
+extern void testlib(void);
+
+int main(void)
+{
+  testlib();
+  return 0;
+}
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos/toplib.c b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos/toplib.c
new file mode 100644
index 0000000..cff1bff
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos/toplib.c
@@ -0,0 +1,6 @@
+extern void testlib(void);
+
+void toplib(void)
+{
+  testlib();
+}
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-project-stderr.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-project-stderr.txt
new file mode 100644
index 0000000..d506645
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-project-stderr.txt
@@ -0,0 +1,13 @@
+^CMake Warning \(dev\) at file-GET_RUNTIME_DEPENDENCIES-project\.cmake:[0-9]+ \(file\):
+  You have used file\(GET_RUNTIME_DEPENDENCIES\) in project mode\.  This is
+  probably not what you intended to do\.  Instead, please consider using it in
+  an install\(CODE\) or install\(SCRIPT\) command\.  For example:
+
+    install\(CODE \[\[
+      file\(GET_RUNTIME_DEPENDENCIES
+        # \.\.\.
+        \)
+      ]]\)
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)
+This warning is for project developers\.  Use -Wno-dev to suppress it\.$
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-project.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-project.cmake
new file mode 100644
index 0000000..842d7ab
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-project.cmake
@@ -0,0 +1 @@
+file(GET_RUNTIME_DEPENDENCIES RESOLVED_DEPENDENCIES_VAR deps)
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-unsupported-result.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-unsupported-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-unsupported-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-unsupported-stderr.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-unsupported-stderr.txt
new file mode 100644
index 0000000..3db835c
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-unsupported-stderr.txt
@@ -0,0 +1,5 @@
+^CMake Error at file-GET_RUNTIME_DEPENDENCIES-unsupported\.cmake:[0-9]+ \(file\):
+  file GET_RUNTIME_DEPENDENCIES is not supported on system "[^
+ ]+"
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-unsupported.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-unsupported.cmake
new file mode 100644
index 0000000..b91eefe
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-unsupported.cmake
@@ -0,0 +1,2 @@
+file(GET_RUNTIME_DEPENDENCIES RESOLVED_DEPENDENCIES_VAR deps)
+message(FATAL_ERROR "This message should not be displayed")
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-all-check.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-all-check.cmake
new file mode 100644
index 0000000..c120ce4
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-all-check.cmake
@@ -0,0 +1,38 @@
+function(check_contents filename contents_regex)
+  if(EXISTS "${CMAKE_INSTALL_PREFIX}/${filename}")
+    file(READ "${CMAKE_INSTALL_PREFIX}/${filename}" contents)
+    if(NOT contents MATCHES "${contents_regex}")
+      string(APPEND RunCMake_TEST_FAILED "File contents:
+  ${contents}
+do not match what we expected:
+  ${contents_regex}
+in file:
+  ${CMAKE_INSTALL_PREFIX}/${filename}\n")
+      set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE)
+    endif()
+  else()
+    string(APPEND RunCMake_TEST_FAILED "File ${CMAKE_INSTALL_PREFIX}/${filename} does not exist")
+    set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE)
+  endif()
+endfunction()
+
+set(_check
+  [=[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-build/root-all/bin/\.conflict/\.\./(lib)?libdir\.dll]=]
+  [=[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-build/root-all/bin/\.search/(lib)?search\.dll]=]
+  [=[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-build/root-all/bin/(lib)?testlib\.dll]=]
+  )
+check_contents(deps/deps1.txt "^${_check}$")
+check_contents(deps/deps2.txt "^${_check}$")
+check_contents(deps/deps3.txt "^${_check}$")
+set(_check
+  [=[(lib)?unresolved\.dll]=]
+  )
+check_contents(deps/udeps1.txt "^${_check}$")
+check_contents(deps/udeps2.txt "^${_check}$")
+check_contents(deps/udeps3.txt "^${_check}$")
+set(_check
+  "^(lib)?conflict\\.dll:[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-build/root-all/bin/\\.conflict/(lib)?conflict\\.dll;[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-build/root-all/bin/(lib)?conflict\\.dll\n$"
+  )
+check_contents(deps/cdeps1.txt "${_check}")
+check_contents(deps/cdeps2.txt "${_check}")
+check_contents(deps/cdeps3.txt "${_check}")
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-conflict-all-result.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-conflict-all-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-conflict-all-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-conflict-all-stderr.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-conflict-all-stderr.txt
new file mode 100644
index 0000000..66ecb93
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-conflict-all-stderr.txt
@@ -0,0 +1,7 @@
+^CMake Error at cmake_install\.cmake:[0-9]+ \(file\):
+  file Multiple conflicting paths found for (lib)?path\.dll:
+
+    [^
+]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-conflict-build/root-all/lib/test1/(lib)?path\.dll
+    [^
+]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-conflict-build/root-all/lib/test2/(lib)?path\.dll$
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-conflict.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-conflict.cmake
new file mode 100644
index 0000000..d413443
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-conflict.cmake
@@ -0,0 +1,47 @@
+enable_language(C)
+
+set(test1_names path)
+set(test2_names path)
+
+file(WRITE "${CMAKE_BINARY_DIR}/path.c" "__declspec(dllexport) void path(void) {}\n")
+add_library(path SHARED "${CMAKE_BINARY_DIR}/path.c")
+
+file(REMOVE "${CMAKE_BINARY_DIR}/test1.c")
+add_library(test1 SHARED "${CMAKE_BINARY_DIR}/test1.c")
+foreach(name ${test1_names})
+  file(APPEND "${CMAKE_BINARY_DIR}/test1.c" "__declspec(dllimport) extern void ${name}(void);\n")
+endforeach()
+file(APPEND "${CMAKE_BINARY_DIR}/test1.c" "__declspec(dllexport) void test1(void)\n{\n")
+foreach(name ${test1_names})
+  file(APPEND "${CMAKE_BINARY_DIR}/test1.c" "  ${name}();\n")
+endforeach()
+file(APPEND "${CMAKE_BINARY_DIR}/test1.c" "}\n")
+
+target_link_libraries(test1 PRIVATE ${test1_names})
+
+file(REMOVE "${CMAKE_BINARY_DIR}/test2.c")
+add_library(test2 SHARED "${CMAKE_BINARY_DIR}/test2.c")
+foreach(name ${test2_names})
+  file(APPEND "${CMAKE_BINARY_DIR}/test2.c" "__declspec(dllimport) extern void ${name}(void);\n")
+endforeach()
+file(APPEND "${CMAKE_BINARY_DIR}/test2.c" "__declspec(dllexport) void test2(void)\n{\n")
+foreach(name ${test2_names})
+  file(APPEND "${CMAKE_BINARY_DIR}/test2.c" "  ${name}();\n")
+endforeach()
+file(APPEND "${CMAKE_BINARY_DIR}/test2.c" "}\n")
+
+target_link_libraries(test2 PRIVATE ${test2_names})
+
+install(TARGETS test1 path DESTINATION lib/test1)
+install(TARGETS test2 path DESTINATION lib/test2)
+
+install(CODE [[
+  file(GET_RUNTIME_DEPENDENCIES
+    LIBRARIES
+      "${CMAKE_INSTALL_PREFIX}/lib/test1/$<TARGET_FILE_NAME:test1>"
+      "${CMAKE_INSTALL_PREFIX}/lib/test2/$<TARGET_FILE_NAME:test2>"
+    PRE_INCLUDE_REGEXES "^(lib)?path\\.dll$"
+    PRE_EXCLUDE_REGEXES ".*"
+    )
+  message(FATAL_ERROR "This message should not be displayed")
+  ]])
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-notfile-all-result.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-notfile-all-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-notfile-all-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-notfile-all-stderr.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-notfile-all-stderr.txt
new file mode 100644
index 0000000..f921409
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-notfile-all-stderr.txt
@@ -0,0 +1,5 @@
+^CMake Error at cmake_install\.cmake:[0-9]+ \(file\):
+  file Failed to run (dumpbin|objdump) on:
+
+    [^
+]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-notfile-build/root-all/bin/(lib)?test\.dll$
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-notfile.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-notfile.cmake
new file mode 100644
index 0000000..6665a3b
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-notfile.cmake
@@ -0,0 +1,28 @@
+enable_language(C)
+
+file(WRITE "${CMAKE_BINARY_DIR}/test.c" "__declspec(dllexport) void test(void) {}\n")
+file(WRITE "${CMAKE_BINARY_DIR}/main.c" [[__declspec(dllimport) extern void test(void);
+
+int main(void)
+{
+  test();
+  return 0;
+}
+]])
+
+add_library(test SHARED "${CMAKE_BINARY_DIR}/test.c")
+add_executable(exe "${CMAKE_BINARY_DIR}/main.c")
+target_link_libraries(exe PRIVATE test)
+
+install(TARGETS exe DESTINATION bin)
+
+install(CODE [[
+  file(MAKE_DIRECTORY "${CMAKE_INSTALL_PREFIX}/bin/$<TARGET_FILE_NAME:test>")
+  file(GET_RUNTIME_DEPENDENCIES
+    EXECUTABLES
+      "${CMAKE_INSTALL_PREFIX}/bin/$<TARGET_FILE_NAME:exe>"
+    PRE_INCLUDE_REGEXES "^(lib)?test\\.dll$"
+    PRE_EXCLUDE_REGEXES ".*"
+    )
+  message(FATAL_ERROR "This message should not be displayed")
+  ]])
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-unresolved-all-result.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-unresolved-all-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-unresolved-all-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-unresolved-all-stderr.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-unresolved-all-stderr.txt
new file mode 100644
index 0000000..a20654c
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-unresolved-all-stderr.txt
@@ -0,0 +1,2 @@
+^CMake Error at cmake_install\.cmake:[0-9]+ \(file\):
+  file Could not resolve file (lib)?unresolved\.dll$
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-unresolved.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-unresolved.cmake
new file mode 100644
index 0000000..4cc74c7
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-unresolved.cmake
@@ -0,0 +1,18 @@
+enable_language(C)
+
+file(WRITE "${CMAKE_BINARY_DIR}/testlib.c" "__declspec(dllimport) extern void unresolved(void);\n__declspec(dllexport) void testlib(void)\n{\n  unresolved();\n}\n")
+add_library(testlib SHARED "${CMAKE_BINARY_DIR}/testlib.c")
+file(WRITE "${CMAKE_BINARY_DIR}/unresolved.c" "__declspec(dllexport) void unresolved(void) {}\n")
+add_library(unresolved SHARED "${CMAKE_BINARY_DIR}/unresolved.c")
+target_link_libraries(testlib PRIVATE unresolved)
+install(TARGETS testlib DESTINATION lib)
+
+install(CODE [[
+  file(GET_RUNTIME_DEPENDENCIES
+    PRE_INCLUDE_REGEXES "^(lib)?unresolved\\.dll$"
+    PRE_EXCLUDE_REGEXES ".*"
+    LIBRARIES
+      "${CMAKE_INSTALL_PREFIX}/lib/$<TARGET_FILE_NAME:testlib>"
+    )
+  message(FATAL_ERROR "This message should not be displayed")
+  ]])
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows.cmake
new file mode 100644
index 0000000..19288d8
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows.cmake
@@ -0,0 +1,114 @@
+enable_language(C)
+
+set(testlib_names
+  preexcluded
+  libdir_postexcluded
+  libdir
+  search_postexcluded
+  search
+  unresolved
+  conflict
+  )
+
+file(REMOVE "${CMAKE_BINARY_DIR}/testlib.c")
+add_library(testlib SHARED "${CMAKE_BINARY_DIR}/testlib.c")
+foreach(name ${testlib_names})
+  file(WRITE "${CMAKE_BINARY_DIR}/${name}.c" "__declspec(dllexport) void ${name}(void) {}\n")
+  add_library(${name} SHARED "${CMAKE_BINARY_DIR}/${name}.c")
+
+  file(APPEND "${CMAKE_BINARY_DIR}/testlib.c" "__declspec(dllimport) extern void ${name}(void);\n")
+endforeach()
+file(APPEND "${CMAKE_BINARY_DIR}/testlib.c" "__declspec(dllexport) void testlib(void)\n{\n")
+foreach(name ${testlib_names})
+  file(APPEND "${CMAKE_BINARY_DIR}/testlib.c" "  ${name}();\n")
+endforeach()
+file(APPEND "${CMAKE_BINARY_DIR}/testlib.c" "}\n")
+
+target_link_libraries(testlib PRIVATE ${testlib_names})
+
+file(WRITE "${CMAKE_BINARY_DIR}/testlib_conflict.c" "__declspec(dllimport) extern void conflict(void);\n__declspec(dllexport) void testlib_conflict(void)\n{\n  conflict();\n}\n")
+add_library(testlib_conflict SHARED "${CMAKE_BINARY_DIR}/testlib_conflict.c")
+target_link_libraries(testlib_conflict PRIVATE conflict)
+
+file(WRITE "${CMAKE_BINARY_DIR}/testlib_noconflict.c" "__declspec(dllimport) extern void libdir(void);\n__declspec(dllexport) void testlib_noconflict(void)\n{\n  libdir();\n}\n")
+add_library(testlib_noconflict SHARED "${CMAKE_BINARY_DIR}/testlib_noconflict.c")
+target_link_libraries(testlib_noconflict PRIVATE libdir)
+
+install(TARGETS testlib libdir_postexcluded libdir conflict testlib_noconflict DESTINATION bin)
+install(TARGETS libdir search_postexcluded search DESTINATION bin/.search) # Prefixing with "." ensures it is the first item after list(SORT)
+install(TARGETS testlib_conflict conflict DESTINATION bin/.conflict)
+
+add_executable(topexe file-GET_RUNTIME_DEPENDENCIES-windows/topexe.c)
+add_library(toplib SHARED file-GET_RUNTIME_DEPENDENCIES-windows/toplib.c)
+add_library(topmod MODULE file-GET_RUNTIME_DEPENDENCIES-windows/toplib.c)
+target_link_libraries(topexe PRIVATE testlib)
+target_link_libraries(toplib PRIVATE testlib)
+target_link_libraries(topmod PRIVATE testlib)
+
+install(TARGETS topexe toplib topmod DESTINATION bin)
+
+install(CODE [[
+  function(exec_get_runtime_dependencies depsfile udepsfile cdepsfile)
+    file(GET_RUNTIME_DEPENDENCIES
+      RESOLVED_DEPENDENCIES_VAR deps
+      UNRESOLVED_DEPENDENCIES_VAR udeps
+      CONFLICTING_DEPENDENCIES_PREFIX cdeps
+      PRE_INCLUDE_REGEXES
+        "^(lib)?testlib\\.dll$"
+        "^(lib)?libdir_postexcluded\\.dll$"
+        "^(lib)?libdir\\.dll$"
+        "^(lib)?search_postexcluded\\.dll$"
+        "^(lib)?search\\.dll$"
+        "^(lib)?unresolved\\.dll$"
+        "^(lib)?conflict\\.dll$"
+        "^kernel32\\.dll$"
+      PRE_EXCLUDE_REGEXES ".*"
+      POST_INCLUDE_REGEXES
+        "^.*/(lib)?testlib\\.dll$"
+        "^.*/(lib)?libdir\\.dll$"
+        "^.*/(lib)?search\\.dll$"
+        "^.*/(lib)?conflict\\.dll$"
+      POST_EXCLUDE_REGEXES ".*"
+      DIRECTORIES
+        "${CMAKE_INSTALL_PREFIX}/bin/.search"
+      ${ARGN}
+      )
+    list(SORT deps)
+    list(SORT udeps)
+    list(SORT cdeps_FILENAMES)
+    file(WRITE "${CMAKE_INSTALL_PREFIX}/deps/${depsfile}" "${deps}")
+    file(WRITE "${CMAKE_INSTALL_PREFIX}/deps/${udepsfile}" "${udeps}")
+    file(WRITE "${CMAKE_INSTALL_PREFIX}/deps/${cdepsfile}" "")
+    foreach(cdep IN LISTS cdeps_FILENAMES)
+      set(cdep_values ${cdeps_${cdep}})
+      list(SORT cdep_values)
+      file(APPEND "${CMAKE_INSTALL_PREFIX}/deps/${cdepsfile}" "${cdep}:${cdep_values}\n")
+    endforeach()
+  endfunction()
+
+  exec_get_runtime_dependencies(
+    deps1.txt udeps1.txt cdeps1.txt
+    EXECUTABLES
+      "${CMAKE_INSTALL_PREFIX}/bin/$<TARGET_FILE_NAME:topexe>"
+    LIBRARIES
+      "${CMAKE_INSTALL_PREFIX}/bin/.conflict/$<TARGET_FILE_NAME:testlib_conflict>"
+      "${CMAKE_INSTALL_PREFIX}/bin/.conflict/../$<TARGET_FILE_NAME:testlib_noconflict>"
+    )
+
+  exec_get_runtime_dependencies(
+    deps2.txt udeps2.txt cdeps2.txt
+    LIBRARIES
+      "${CMAKE_INSTALL_PREFIX}/bin/$<TARGET_FILE_NAME:toplib>"
+      "${CMAKE_INSTALL_PREFIX}/bin/.conflict/$<TARGET_FILE_NAME:testlib_conflict>"
+      "${CMAKE_INSTALL_PREFIX}/bin/.conflict/../$<TARGET_FILE_NAME:testlib_noconflict>"
+    )
+
+  exec_get_runtime_dependencies(
+    deps3.txt udeps3.txt cdeps3.txt
+    MODULES
+      "${CMAKE_INSTALL_PREFIX}/bin/$<TARGET_FILE_NAME:topmod>"
+    LIBRARIES
+      "${CMAKE_INSTALL_PREFIX}/bin/.conflict/$<TARGET_FILE_NAME:testlib_conflict>"
+      "${CMAKE_INSTALL_PREFIX}/bin/.conflict/../$<TARGET_FILE_NAME:testlib_noconflict>"
+    )
+  ]])
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows/topexe.c b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows/topexe.c
new file mode 100644
index 0000000..713b8eb
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows/topexe.c
@@ -0,0 +1,7 @@
+__declspec(dllimport) extern void testlib(void);
+
+int main(void)
+{
+  testlib();
+  return 0;
+}
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows/toplib.c b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows/toplib.c
new file mode 100644
index 0000000..6997175
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows/toplib.c
@@ -0,0 +1,6 @@
+__declspec(dllimport) extern void testlib(void);
+
+__declspec(dllexport) void toplib(void)
+{
+  testlib();
+}
diff --git a/Tests/Tutorial/Complete/tutorial.cxx b/Tests/Tutorial/Complete/tutorial.cxx
index 443d195..4451cbd 100644
--- a/Tests/Tutorial/Complete/tutorial.cxx
+++ b/Tests/Tutorial/Complete/tutorial.cxx
@@ -10,7 +10,7 @@
 {
   if (argc < 2) {
     std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "."
-              << Tutorial_VERSION_MAJOR << std::endl;
+              << Tutorial_VERSION_MINOR << std::endl;
     std::cout << "Usage: " << argv[0] << " number" << std::endl;
     return 1;
   }
diff --git a/Tests/Tutorial/MultiPackage/tutorial.cxx b/Tests/Tutorial/MultiPackage/tutorial.cxx
index 443d195..4451cbd 100644
--- a/Tests/Tutorial/MultiPackage/tutorial.cxx
+++ b/Tests/Tutorial/MultiPackage/tutorial.cxx
@@ -10,7 +10,7 @@
 {
   if (argc < 2) {
     std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "."
-              << Tutorial_VERSION_MAJOR << std::endl;
+              << Tutorial_VERSION_MINOR << std::endl;
     std::cout << "Usage: " << argv[0] << " number" << std::endl;
     return 1;
   }
diff --git a/Tests/Tutorial/Step10/tutorial.cxx b/Tests/Tutorial/Step10/tutorial.cxx
index 443d195..4451cbd 100644
--- a/Tests/Tutorial/Step10/tutorial.cxx
+++ b/Tests/Tutorial/Step10/tutorial.cxx
@@ -10,7 +10,7 @@
 {
   if (argc < 2) {
     std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "."
-              << Tutorial_VERSION_MAJOR << std::endl;
+              << Tutorial_VERSION_MINOR << std::endl;
     std::cout << "Usage: " << argv[0] << " number" << std::endl;
     return 1;
   }
diff --git a/Tests/Tutorial/Step11/tutorial.cxx b/Tests/Tutorial/Step11/tutorial.cxx
index 3768855..38d4a79 100644
--- a/Tests/Tutorial/Step11/tutorial.cxx
+++ b/Tests/Tutorial/Step11/tutorial.cxx
@@ -10,7 +10,7 @@
 {
   if (argc < 2) {
     std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "."
-              << Tutorial_VERSION_MAJOR << std::endl;
+              << Tutorial_VERSION_MINOR << std::endl;
     std::cout << "Usage: " << argv[0] << " number" << std::endl;
     return 1;
   }
diff --git a/Tests/Tutorial/Step2/tutorial.cxx b/Tests/Tutorial/Step2/tutorial.cxx
index 75b7d67..5ba34e8 100644
--- a/Tests/Tutorial/Step2/tutorial.cxx
+++ b/Tests/Tutorial/Step2/tutorial.cxx
@@ -9,7 +9,7 @@
 {
   if (argc < 2) {
     std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "."
-              << Tutorial_VERSION_MAJOR << std::endl;
+              << Tutorial_VERSION_MINOR << std::endl;
     std::cout << "Usage: " << argv[0] << " number" << std::endl;
     return 1;
   }
diff --git a/Tests/Tutorial/Step3/tutorial.cxx b/Tests/Tutorial/Step3/tutorial.cxx
index 1d5742d..c2b89df 100644
--- a/Tests/Tutorial/Step3/tutorial.cxx
+++ b/Tests/Tutorial/Step3/tutorial.cxx
@@ -13,7 +13,7 @@
 {
   if (argc < 2) {
     std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "."
-              << Tutorial_VERSION_MAJOR << std::endl;
+              << Tutorial_VERSION_MINOR << std::endl;
     std::cout << "Usage: " << argv[0] << " number" << std::endl;
     return 1;
   }
diff --git a/Tests/Tutorial/Step4/tutorial.cxx b/Tests/Tutorial/Step4/tutorial.cxx
index 1d5742d..c2b89df 100644
--- a/Tests/Tutorial/Step4/tutorial.cxx
+++ b/Tests/Tutorial/Step4/tutorial.cxx
@@ -13,7 +13,7 @@
 {
   if (argc < 2) {
     std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "."
-              << Tutorial_VERSION_MAJOR << std::endl;
+              << Tutorial_VERSION_MINOR << std::endl;
     std::cout << "Usage: " << argv[0] << " number" << std::endl;
     return 1;
   }
diff --git a/Tests/Tutorial/Step5/tutorial.cxx b/Tests/Tutorial/Step5/tutorial.cxx
index 1d5742d..c2b89df 100644
--- a/Tests/Tutorial/Step5/tutorial.cxx
+++ b/Tests/Tutorial/Step5/tutorial.cxx
@@ -13,7 +13,7 @@
 {
   if (argc < 2) {
     std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "."
-              << Tutorial_VERSION_MAJOR << std::endl;
+              << Tutorial_VERSION_MINOR << std::endl;
     std::cout << "Usage: " << argv[0] << " number" << std::endl;
     return 1;
   }
diff --git a/Tests/Tutorial/Step6/tutorial.cxx b/Tests/Tutorial/Step6/tutorial.cxx
index 1d5742d..c2b89df 100644
--- a/Tests/Tutorial/Step6/tutorial.cxx
+++ b/Tests/Tutorial/Step6/tutorial.cxx
@@ -13,7 +13,7 @@
 {
   if (argc < 2) {
     std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "."
-              << Tutorial_VERSION_MAJOR << std::endl;
+              << Tutorial_VERSION_MINOR << std::endl;
     std::cout << "Usage: " << argv[0] << " number" << std::endl;
     return 1;
   }
diff --git a/Tests/Tutorial/Step7/tutorial.cxx b/Tests/Tutorial/Step7/tutorial.cxx
index 1d5742d..c2b89df 100644
--- a/Tests/Tutorial/Step7/tutorial.cxx
+++ b/Tests/Tutorial/Step7/tutorial.cxx
@@ -13,7 +13,7 @@
 {
   if (argc < 2) {
     std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "."
-              << Tutorial_VERSION_MAJOR << std::endl;
+              << Tutorial_VERSION_MINOR << std::endl;
     std::cout << "Usage: " << argv[0] << " number" << std::endl;
     return 1;
   }
diff --git a/Tests/Tutorial/Step8/tutorial.cxx b/Tests/Tutorial/Step8/tutorial.cxx
index 1d5742d..c2b89df 100644
--- a/Tests/Tutorial/Step8/tutorial.cxx
+++ b/Tests/Tutorial/Step8/tutorial.cxx
@@ -13,7 +13,7 @@
 {
   if (argc < 2) {
     std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "."
-              << Tutorial_VERSION_MAJOR << std::endl;
+              << Tutorial_VERSION_MINOR << std::endl;
     std::cout << "Usage: " << argv[0] << " number" << std::endl;
     return 1;
   }
diff --git a/Tests/Tutorial/Step9/tutorial.cxx b/Tests/Tutorial/Step9/tutorial.cxx
index 73e67a9..6c41859 100644
--- a/Tests/Tutorial/Step9/tutorial.cxx
+++ b/Tests/Tutorial/Step9/tutorial.cxx
@@ -14,7 +14,7 @@
 {
   if (argc < 2) {
     std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "."
-              << Tutorial_VERSION_MAJOR << std::endl;
+              << Tutorial_VERSION_MINOR << std::endl;
     std::cout << "Usage: " << argv[0] << " number" << std::endl;
     return 1;
   }
diff --git a/bootstrap b/bootstrap
index 38fa32b..c2194fe 100755
--- a/bootstrap
+++ b/bootstrap
@@ -261,6 +261,17 @@
   cmAddSubDirectoryCommand \
   cmAddTestCommand \
   cmArgumentParser \
+  cmBinUtilsLinker \
+  cmBinUtilsLinuxELFGetRuntimeDependenciesTool \
+  cmBinUtilsLinuxELFLinker \
+  cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool \
+  cmBinUtilsMacOSMachOGetRuntimeDependenciesTool \
+  cmBinUtilsMacOSMachOLinker \
+  cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool \
+  cmBinUtilsWindowsPEGetRuntimeDependenciesTool \
+  cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool \
+  cmBinUtilsWindowsPELinker \
+  cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool \
   cmBreakCommand \
   cmBuildCommand \
   cmCMakeMinimumRequired \
@@ -357,6 +368,8 @@
   cmInstallTargetGenerator \
   cmInstallTargetsCommand \
   cmInstalledFile \
+  cmLDConfigLDConfigTool \
+  cmLDConfigTool \
   cmLinkDirectoriesCommand \
   cmLinkItem \
   cmLinkLineComputer \
@@ -388,12 +401,12 @@
   cmPolicies \
   cmProcessOutput \
   cmProjectCommand \
-  cmProperty \
   cmPropertyDefinition \
   cmPropertyDefinitionMap \
   cmPropertyMap \
   cmReturnCommand \
   cmRulePlaceholderExpander \
+  cmRuntimeDependencyArchive \
   cmScriptGenerator \
   cmSearchPath \
   cmSeparateArgumentsCommand \
@@ -443,7 +456,9 @@
 if ${cmake_system_mingw}; then
   CMAKE_CXX_SOURCES="${CMAKE_CXX_SOURCES}\
     cmGlobalMSYSMakefileGenerator \
-    cmGlobalMinGWMakefileGenerator"
+    cmGlobalMinGWMakefileGenerator \
+    cmVSSetupHelper \
+  "
 fi
 
 LexerParser_CXX_SOURCES="\
@@ -1370,7 +1385,7 @@
 uv_c_flags=""
 if ${cmake_system_mingw}; then
   uv_c_flags="${uv_c_flags} -DWIN32_LEAN_AND_MEAN -D_WIN32_WINNT=0x0600"
-  libs="${libs} -lws2_32 -lpsapi -liphlpapi -lshell32 -luserenv"
+  libs="${libs} -lws2_32 -lpsapi -liphlpapi -lshell32 -luserenv -lole32 -loleaut32"
 else
   uv_c_flags="${uv_c_flags} -DCMAKE_BOOTSTRAP"
   case "${cmake_system}" in