Browse Source

Merge topic 'get-runtime-dependencies'

54ec4c1e79 GetPrerequisites: Add deprecation notice
1f9ef25130 file: Add GET_RUNTIME_DEPENDENCIES mode

Acked-by: Kitware Robot <[email protected]>
Merge-request: !3335
Kyle Edwards 6 years ago
parent
commit
14254e9ea8
89 changed files with 3690 additions and 3 deletions
  1. 268 0
      Help/command/file.rst
  2. 9 0
      Help/release/dev/get-runtime-dependencies.rst
  3. 4 0
      Modules/GetPrerequisites.cmake
  4. 28 0
      Source/CMakeLists.txt
  5. 15 0
      Source/cmBinUtilsLinker.cxx
  6. 30 0
      Source/cmBinUtilsLinker.h
  7. 18 0
      Source/cmBinUtilsLinuxELFGetRuntimeDependenciesTool.cxx
  8. 30 0
      Source/cmBinUtilsLinuxELFGetRuntimeDependenciesTool.h
  9. 177 0
      Source/cmBinUtilsLinuxELFLinker.cxx
  10. 44 0
      Source/cmBinUtilsLinuxELFLinker.h
  11. 84 0
      Source/cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.cxx
  12. 26 0
      Source/cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.h
  13. 19 0
      Source/cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.cxx
  14. 29 0
      Source/cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.h
  15. 228 0
      Source/cmBinUtilsMacOSMachOLinker.cxx
  16. 59 0
      Source/cmBinUtilsMacOSMachOLinker.h
  17. 100 0
      Source/cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.cxx
  18. 25 0
      Source/cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.h
  19. 67 0
      Source/cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.cxx
  20. 25 0
      Source/cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.h
  21. 18 0
      Source/cmBinUtilsWindowsPEGetRuntimeDependenciesTool.cxx
  22. 28 0
      Source/cmBinUtilsWindowsPEGetRuntimeDependenciesTool.h
  23. 121 0
      Source/cmBinUtilsWindowsPELinker.cxx
  24. 33 0
      Source/cmBinUtilsWindowsPELinker.h
  25. 67 0
      Source/cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.cxx
  26. 25 0
      Source/cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.h
  27. 175 0
      Source/cmFileCommand.cxx
  28. 2 0
      Source/cmFileCommand.h
  29. 70 0
      Source/cmLDConfigLDConfigTool.cxx
  30. 22 0
      Source/cmLDConfigLDConfigTool.h
  31. 9 0
      Source/cmLDConfigTool.cxx
  32. 24 0
      Source/cmLDConfigTool.h
  33. 378 0
      Source/cmRuntimeDependencyArchive.cxx
  34. 70 0
      Source/cmRuntimeDependencyArchive.h
  35. 1 1
      Tests/RunCMake/CMakeLists.txt
  36. 3 0
      Tests/RunCMake/install/CMakeLists.txt
  37. 30 0
      Tests/RunCMake/install/RunCMakeTest.cmake
  38. 1 0
      Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs1-result.txt
  39. 18 0
      Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs1-stderr.txt
  40. 2 0
      Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs1.cmake
  41. 1 0
      Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs2-result.txt
  42. 18 0
      Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs2-stderr.txt
  43. 2 0
      Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs2.cmake
  44. 44 0
      Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-all-check.cmake
  45. 119 0
      Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-all-stderr.txt
  46. 1 0
      Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-conflict-all-result.txt
  47. 7 0
      Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-conflict-all-stderr.txt
  48. 54 0
      Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-conflict.cmake
  49. 1 0
      Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-notfile-all-result.txt
  50. 5 0
      Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-notfile-all-stderr.txt
  51. 29 0
      Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-notfile.cmake
  52. 1 0
      Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-unresolved-all-result.txt
  53. 2 0
      Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-unresolved-all-stderr.txt
  54. 18 0
      Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-unresolved.cmake
  55. 168 0
      Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux.cmake
  56. 9 0
      Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux/topexe.c
  57. 8 0
      Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux/toplib.c
  58. 157 0
      Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-all-check.cmake
  59. 1 0
      Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-conflict-all-result.txt
  60. 7 0
      Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-conflict-all-stderr.txt
  61. 55 0
      Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-conflict.cmake
  62. 1 0
      Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-notfile-all-result.txt
  63. 5 0
      Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-notfile-all-stderr.txt
  64. 30 0
      Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-notfile.cmake
  65. 1 0
      Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-unresolved-all-result.txt
  66. 2 0
      Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-unresolved-all-stderr.txt
  67. 18 0
      Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-unresolved.cmake
  68. 216 0
      Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos.cmake
  69. 7 0
      Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos/topexe.c
  70. 6 0
      Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos/toplib.c
  71. 13 0
      Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-project-stderr.txt
  72. 1 0
      Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-project.cmake
  73. 1 0
      Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-unsupported-result.txt
  74. 5 0
      Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-unsupported-stderr.txt
  75. 2 0
      Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-unsupported.cmake
  76. 38 0
      Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-all-check.cmake
  77. 1 0
      Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-conflict-all-result.txt
  78. 7 0
      Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-conflict-all-stderr.txt
  79. 47 0
      Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-conflict.cmake
  80. 1 0
      Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-notfile-all-result.txt
  81. 5 0
      Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-notfile-all-stderr.txt
  82. 28 0
      Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-notfile.cmake
  83. 1 0
      Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-unresolved-all-result.txt
  84. 2 0
      Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-unresolved-all-stderr.txt
  85. 18 0
      Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-unresolved.cmake
  86. 114 0
      Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows.cmake
  87. 7 0
      Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows/topexe.c
  88. 6 0
      Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows/toplib.c
  89. 18 2
      bootstrap

+ 268 - 0
Help/command/file.rst

@@ -13,6 +13,7 @@ Synopsis
     file(`STRINGS`_ <filename> <out-var> [...])
     file(`STRINGS`_ <filename> <out-var> [...])
     file(`\<HASH\> <HASH_>`_ <filename> <out-var>)
     file(`\<HASH\> <HASH_>`_ <filename> <out-var>)
     file(`TIMESTAMP`_ <filename> <out-var> [...])
     file(`TIMESTAMP`_ <filename> <out-var> [...])
+    file(`GET_RUNTIME_DEPENDENCIES`_ [...])
 
 
   `Writing`_
   `Writing`_
     file({`WRITE`_ | `APPEND`_} <filename> <content>...)
     file({`WRITE`_ | `APPEND`_} <filename> <content>...)
@@ -130,6 +131,273 @@ timestamp variable will be set to the empty string ("").
 See the :command:`string(TIMESTAMP)` command for documentation of
 See the :command:`string(TIMESTAMP)` command for documentation of
 the ``<format>`` and ``UTC`` options.
 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
 Writing
 ^^^^^^^
 ^^^^^^^
 
 

+ 9 - 0
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)`.

+ 4 - 0
Modules/GetPrerequisites.cmake

@@ -5,6 +5,10 @@
 GetPrerequisites
 GetPrerequisites
 ----------------
 ----------------
 
 
+.. deprecated:: 3.16
+
+  Use :command:`file(GET_RUNTIME_DEPENDENCIES)` instead.
+
 Functions to analyze and list executable file prerequisites.
 Functions to analyze and list executable file prerequisites.
 
 
 This module provides functions to list the .dll, .dylib or .so files
 This module provides functions to list the .dll, .dylib or .so files

+ 28 - 0
Source/CMakeLists.txt

@@ -146,6 +146,28 @@ set(SRCS
   cmArgumentParser.cxx
   cmArgumentParser.cxx
   cmArgumentParser.h
   cmArgumentParser.h
   cmBase32.cxx
   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.cxx
   cmCacheManager.h
   cmCacheManager.h
   cmCLocaleEnvironmentScope.h
   cmCLocaleEnvironmentScope.h
@@ -295,6 +317,10 @@ set(SRCS
   cmInstallTargetGenerator.cxx
   cmInstallTargetGenerator.cxx
   cmInstallDirectoryGenerator.h
   cmInstallDirectoryGenerator.h
   cmInstallDirectoryGenerator.cxx
   cmInstallDirectoryGenerator.cxx
+  cmLDConfigLDConfigTool.cxx
+  cmLDConfigLDConfigTool.h
+  cmLDConfigTool.cxx
+  cmLDConfigTool.h
   cmLinkedTree.h
   cmLinkedTree.h
   cmLinkItem.cxx
   cmLinkItem.cxx
   cmLinkItem.h
   cmLinkItem.h
@@ -360,6 +386,8 @@ set(SRCS
   cmQtAutoRcc.h
   cmQtAutoRcc.h
   cmRST.cxx
   cmRST.cxx
   cmRST.h
   cmRST.h
+  cmRuntimeDependencyArchive.cxx
+  cmRuntimeDependencyArchive.h
   cmScriptGenerator.h
   cmScriptGenerator.h
   cmScriptGenerator.cxx
   cmScriptGenerator.cxx
   cmSourceFile.cxx
   cmSourceFile.cxx

+ 15 - 0
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);
+}

+ 30 - 0
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

+ 18 - 0
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);
+}

+ 30 - 0
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

+ 177 - 0
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;
+}

+ 44 - 0
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

+ 84 - 0
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;
+}

+ 26 - 0
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

+ 19 - 0
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);
+}

+ 29 - 0
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

+ 228 - 0
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;
+}

+ 59 - 0
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

+ 100 - 0
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;
+}

+ 25 - 0
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

+ 67 - 0
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;
+}

+ 25 - 0
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

+ 18 - 0
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);
+}

+ 28 - 0
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

+ 121 - 0
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;
+}

+ 33 - 0
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

+ 67 - 0
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;
+}

+ 25 - 0
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

+ 175 - 0
Source/cmFileCommand.cxx

@@ -12,7 +12,9 @@
 #include <assert.h>
 #include <assert.h>
 #include <cmath>
 #include <cmath>
 #include <ctype.h>
 #include <ctype.h>
+#include <map>
 #include <memory> // IWYU pragma: keep
 #include <memory> // IWYU pragma: keep
+#include <set>
 #include <sstream>
 #include <sstream>
 #include <stdio.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdlib.h>
@@ -34,6 +36,8 @@
 #include "cmMessageType.h"
 #include "cmMessageType.h"
 #include "cmPolicies.h"
 #include "cmPolicies.h"
 #include "cmRange.h"
 #include "cmRange.h"
+#include "cmRuntimeDependencyArchive.h"
+#include "cmState.h"
 #include "cmSystemTools.h"
 #include "cmSystemTools.h"
 #include "cmTimestamp.h"
 #include "cmTimestamp.h"
 #include "cm_sys_stat.h"
 #include "cm_sys_stat.h"
@@ -184,6 +188,9 @@ bool cmFileCommand::InitialPass(std::vector<std::string> const& args,
   if (subCommand == "CREATE_LINK") {
   if (subCommand == "CREATE_LINK") {
     return this->HandleCreateLinkCommand(args);
     return this->HandleCreateLinkCommand(args);
   }
   }
+  if (subCommand == "GET_RUNTIME_DEPENDENCIES") {
+    return this->HandleGetRuntimeDependenciesCommand(args);
+  }
 
 
   std::string e = "does not recognize sub-command " + subCommand;
   std::string e = "does not recognize sub-command " + subCommand;
   this->SetError(e);
   this->SetError(e);
@@ -2690,3 +2697,171 @@ bool cmFileCommand::HandleCreateLinkCommand(
 
 
   return true;
   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;
+}

+ 2 - 0
Source/cmFileCommand.h

@@ -62,6 +62,8 @@ protected:
   bool HandleSizeCommand(std::vector<std::string> const& args);
   bool HandleSizeCommand(std::vector<std::string> const& args);
   bool HandleReadSymlinkCommand(std::vector<std::string> const& args);
   bool HandleReadSymlinkCommand(std::vector<std::string> const& args);
   bool HandleCreateLinkCommand(std::vector<std::string> const& args);
   bool HandleCreateLinkCommand(std::vector<std::string> const& args);
+  bool HandleGetRuntimeDependenciesCommand(
+    std::vector<std::string> const& args);
 
 
 private:
 private:
   void AddEvaluationFile(const std::string& inputName,
   void AddEvaluationFile(const std::string& inputName,

+ 70 - 0
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;
+}

+ 22 - 0
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

+ 9 - 0
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)
+{
+}

+ 24 - 0
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

+ 378 - 0
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;
+}

+ 70 - 0
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

+ 1 - 1
Tests/RunCMake/CMakeLists.txt

@@ -408,7 +408,7 @@ else()
   set(NO_NAMELINK 0)
   set(NO_NAMELINK 0)
 endif()
 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})
 add_RunCMake_test(CPackCommandLine)
 add_RunCMake_test(CPackCommandLine)
 add_RunCMake_test(CPackConfig)
 add_RunCMake_test(CPackConfig)
 add_RunCMake_test(CPackInstallProperties)
 add_RunCMake_test(CPackInstallProperties)

+ 3 - 0
Tests/RunCMake/install/CMakeLists.txt

@@ -1,3 +1,6 @@
 cmake_minimum_required(VERSION 3.4)
 cmake_minimum_required(VERSION 3.4)
+if(RunCMake_TEST MATCHES "^file-GET_RUNTIME_DEPENDENCIES")
+  cmake_policy(SET CMP0087 NEW)
+endif()
 project(${RunCMake_TEST} NONE)
 project(${RunCMake_TEST} NONE)
 include(${RunCMake_TEST}.cmake)
 include(${RunCMake_TEST}.cmake)

+ 30 - 0
Tests/RunCMake/install/RunCMakeTest.cmake

@@ -139,6 +139,36 @@ run_install_test(FILES-PERMISSIONS)
 run_install_test(TARGETS-RPATH)
 run_install_test(TARGETS-RPATH)
 run_install_test(InstallRequiredSystemLibraries)
 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)
 set(run_install_test_components 1)
 run_install_test(FILES-EXCLUDE_FROM_ALL)
 run_install_test(FILES-EXCLUDE_FROM_ALL)
 run_install_test(TARGETS-EXCLUDE_FROM_ALL)
 run_install_test(TARGETS-EXCLUDE_FROM_ALL)

+ 1 - 0
Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs1-result.txt

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

+ 18 - 0
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\)$

+ 2 - 0
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")

+ 1 - 0
Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs2-result.txt

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

+ 18 - 0
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\)$

+ 2 - 0
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")

+ 44 - 0
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}")

+ 119 - 0
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\)$

+ 1 - 0
Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-conflict-all-result.txt

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

+ 7 - 0
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$

+ 54 - 0
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")
+  ]])

+ 1 - 0
Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-notfile-all-result.txt

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

+ 5 - 0
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$

+ 29 - 0
Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-notfile.cmake

@@ -0,0 +1,29 @@
+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")
+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")
+  ]])

+ 1 - 0
Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-unresolved-all-result.txt

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

+ 2 - 0
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$

+ 18 - 0
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")
+  ]])

+ 168 - 0
Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux.cmake

@@ -0,0 +1,168 @@
+enable_language(C)
+
+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" # This must be double-escaped because of issue #19225.
+  "${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" # This must be double-escaped because of issue #19225.
+  "\\\$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>"
+    )
+  ]])

+ 9 - 0
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;
+}

+ 8 - 0
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();
+}

+ 157 - 0
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}")

+ 1 - 0
Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-conflict-all-result.txt

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

+ 7 - 0
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$

+ 55 - 0
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")
+  ]])

+ 1 - 0
Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-notfile-all-result.txt

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

+ 5 - 0
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$

+ 30 - 0
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")
+  ]])

+ 1 - 0
Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-unresolved-all-result.txt

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

+ 2 - 0
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$

+ 18 - 0
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")
+  ]])

+ 216 - 0
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>"
+    )
+  ]])

+ 7 - 0
Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos/topexe.c

@@ -0,0 +1,7 @@
+extern void testlib(void);
+
+int main(void)
+{
+  testlib();
+  return 0;
+}

+ 6 - 0
Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos/toplib.c

@@ -0,0 +1,6 @@
+extern void testlib(void);
+
+void toplib(void)
+{
+  testlib();
+}

+ 13 - 0
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\.$

+ 1 - 0
Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-project.cmake

@@ -0,0 +1 @@
+file(GET_RUNTIME_DEPENDENCIES RESOLVED_DEPENDENCIES_VAR deps)

+ 1 - 0
Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-unsupported-result.txt

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

+ 5 - 0
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\)$

+ 2 - 0
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")

+ 38 - 0
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}")

+ 1 - 0
Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-conflict-all-result.txt

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

+ 7 - 0
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$

+ 47 - 0
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")
+  ]])

+ 1 - 0
Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-notfile-all-result.txt

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

+ 5 - 0
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$

+ 28 - 0
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")
+  ]])

+ 1 - 0
Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-unresolved-all-result.txt

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

+ 2 - 0
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$

+ 18 - 0
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")
+  ]])

+ 114 - 0
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>"
+    )
+  ]])

+ 7 - 0
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;
+}

+ 6 - 0
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();
+}

+ 18 - 2
bootstrap

@@ -261,6 +261,17 @@ CMAKE_CXX_SOURCES="\
   cmAddSubDirectoryCommand \
   cmAddSubDirectoryCommand \
   cmAddTestCommand \
   cmAddTestCommand \
   cmArgumentParser \
   cmArgumentParser \
+  cmBinUtilsLinker \
+  cmBinUtilsLinuxELFGetRuntimeDependenciesTool \
+  cmBinUtilsLinuxELFLinker \
+  cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool \
+  cmBinUtilsMacOSMachOGetRuntimeDependenciesTool \
+  cmBinUtilsMacOSMachOLinker \
+  cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool \
+  cmBinUtilsWindowsPEGetRuntimeDependenciesTool \
+  cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool \
+  cmBinUtilsWindowsPELinker \
+  cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool \
   cmBreakCommand \
   cmBreakCommand \
   cmBuildCommand \
   cmBuildCommand \
   cmCMakeMinimumRequired \
   cmCMakeMinimumRequired \
@@ -357,6 +368,8 @@ CMAKE_CXX_SOURCES="\
   cmInstallTargetGenerator \
   cmInstallTargetGenerator \
   cmInstallTargetsCommand \
   cmInstallTargetsCommand \
   cmInstalledFile \
   cmInstalledFile \
+  cmLDConfigLDConfigTool \
+  cmLDConfigTool \
   cmLinkDirectoriesCommand \
   cmLinkDirectoriesCommand \
   cmLinkItem \
   cmLinkItem \
   cmLinkLineComputer \
   cmLinkLineComputer \
@@ -394,6 +407,7 @@ CMAKE_CXX_SOURCES="\
   cmPropertyMap \
   cmPropertyMap \
   cmReturnCommand \
   cmReturnCommand \
   cmRulePlaceholderExpander \
   cmRulePlaceholderExpander \
+  cmRuntimeDependencyArchive \
   cmScriptGenerator \
   cmScriptGenerator \
   cmSearchPath \
   cmSearchPath \
   cmSeparateArgumentsCommand \
   cmSeparateArgumentsCommand \
@@ -443,7 +457,9 @@ CMAKE_CXX_SOURCES="\
 if ${cmake_system_mingw}; then
 if ${cmake_system_mingw}; then
   CMAKE_CXX_SOURCES="${CMAKE_CXX_SOURCES}\
   CMAKE_CXX_SOURCES="${CMAKE_CXX_SOURCES}\
     cmGlobalMSYSMakefileGenerator \
     cmGlobalMSYSMakefileGenerator \
-    cmGlobalMinGWMakefileGenerator"
+    cmGlobalMinGWMakefileGenerator \
+    cmVSSetupHelper \
+  "
 fi
 fi
 
 
 LexerParser_CXX_SOURCES="\
 LexerParser_CXX_SOURCES="\
@@ -1370,7 +1386,7 @@ libs=""
 uv_c_flags=""
 uv_c_flags=""
 if ${cmake_system_mingw}; then
 if ${cmake_system_mingw}; then
   uv_c_flags="${uv_c_flags} -DWIN32_LEAN_AND_MEAN -D_WIN32_WINNT=0x0600"
   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
 else
   uv_c_flags="${uv_c_flags} -DCMAKE_BOOTSTRAP"
   uv_c_flags="${uv_c_flags} -DCMAKE_BOOTSTRAP"
   case "${cmake_system}" in
   case "${cmake_system}" in