浏览代码

find_* commands: add control over Windows registry views

Fixes: #22775
Marc Chevrier 3 年之前
父节点
当前提交
8d7e80cf3d
共有 100 个文件被更改,包括 1644 次插入57 次删除
  1. 13 0
      Help/command/FIND_XXX.txt
  2. 43 0
      Help/command/FIND_XXX_REGISTRY_QUERY.txt
  3. 41 0
      Help/command/FIND_XXX_REGISTRY_VIEW.txt
  4. 2 2
      Help/command/add_custom_command.rst
  5. 2 0
      Help/command/find_file.rst
  6. 2 0
      Help/command/find_library.rst
  7. 31 7
      Help/command/find_package.rst
  8. 2 0
      Help/command/find_path.rst
  9. 2 0
      Help/command/find_program.rst
  10. 1 0
      Help/manual/cmake-policies.7.rst
  11. 39 0
      Help/policy/CMP0134.rst
  12. 6 0
      Help/release/dev/find_item-query-windows-registry.rst
  13. 15 0
      Source/cmFindBase.cxx
  14. 12 0
      Source/cmFindCommon.cxx
  15. 2 0
      Source/cmFindCommon.h
  16. 21 0
      Source/cmFindPackageCommand.cxx
  17. 1 0
      Source/cmFindPackageCommand.h
  18. 14 0
      Source/cmFindProgramCommand.cxx
  19. 4 0
      Source/cmPolicies.h
  20. 10 20
      Source/cmSearchPath.cxx
  21. 255 26
      Source/cmWindowsRegistry.cxx
  22. 27 2
      Source/cmWindowsRegistry.h
  23. 0 0
      Tests/RunCMake/find_file/32bit/file.txt
  24. 0 0
      Tests/RunCMake/find_file/32bit/file32bit.txt
  25. 0 0
      Tests/RunCMake/find_file/64bit/file.txt
  26. 0 0
      Tests/RunCMake/find_file/64bit/file64bit.txt
  27. 1 0
      Tests/RunCMake/find_file/REGISTRY_VIEW-no-view-result.txt
  28. 4 0
      Tests/RunCMake/find_file/REGISTRY_VIEW-no-view-stderr.txt
  29. 2 0
      Tests/RunCMake/find_file/REGISTRY_VIEW-no-view.cmake
  30. 1 0
      Tests/RunCMake/find_file/REGISTRY_VIEW-wrong-view-result.txt
  31. 4 0
      Tests/RunCMake/find_file/REGISTRY_VIEW-wrong-view-stderr.txt
  32. 2 0
      Tests/RunCMake/find_file/REGISTRY_VIEW-wrong-view.cmake
  33. 218 0
      Tests/RunCMake/find_file/Registry-query.cmake
  34. 28 0
      Tests/RunCMake/find_file/RunCMakeTest.cmake
  35. 0 0
      Tests/RunCMake/find_file/default.32bit/file.txt
  36. 0 0
      Tests/RunCMake/find_file/default.32bit/file32bit.txt
  37. 0 0
      Tests/RunCMake/find_file/default.64bit/file.txt
  38. 0 0
      Tests/RunCMake/find_file/default.64bit/file64bit.txt
  39. 二进制
      Tests/RunCMake/find_file/registry_host32bit.reg
  40. 二进制
      Tests/RunCMake/find_file/registry_host64bit.reg
  41. 0 0
      Tests/RunCMake/find_library/32bit/file.lib
  42. 0 0
      Tests/RunCMake/find_library/32bit/file32bit.lib
  43. 0 0
      Tests/RunCMake/find_library/64bit/file.lib
  44. 0 0
      Tests/RunCMake/find_library/64bit/file64bit.lib
  45. 1 0
      Tests/RunCMake/find_library/REGISTRY_VIEW-no-view-result.txt
  46. 4 0
      Tests/RunCMake/find_library/REGISTRY_VIEW-no-view-stderr.txt
  47. 2 0
      Tests/RunCMake/find_library/REGISTRY_VIEW-no-view.cmake
  48. 1 0
      Tests/RunCMake/find_library/REGISTRY_VIEW-wrong-view-result.txt
  49. 4 0
      Tests/RunCMake/find_library/REGISTRY_VIEW-wrong-view-stderr.txt
  50. 2 0
      Tests/RunCMake/find_library/REGISTRY_VIEW-wrong-view.cmake
  51. 218 0
      Tests/RunCMake/find_library/Registry-query.cmake
  52. 28 0
      Tests/RunCMake/find_library/RunCMakeTest.cmake
  53. 0 0
      Tests/RunCMake/find_library/default.32bit/file.lib
  54. 0 0
      Tests/RunCMake/find_library/default.32bit/file32bit.lib
  55. 0 0
      Tests/RunCMake/find_library/default.64bit/file.lib
  56. 0 0
      Tests/RunCMake/find_library/default.64bit/file64bit.lib
  57. 二进制
      Tests/RunCMake/find_library/registry_host32bit.reg
  58. 二进制
      Tests/RunCMake/find_library/registry_host64bit.reg
  59. 4 0
      Tests/RunCMake/find_package/32bit/RegistryView32Config.cmake
  60. 4 0
      Tests/RunCMake/find_package/32bit/RegistryViewConfig.cmake
  61. 4 0
      Tests/RunCMake/find_package/64bit/RegistryView64Config.cmake
  62. 4 0
      Tests/RunCMake/find_package/64bit/RegistryViewConfig.cmake
  63. 11 0
      Tests/RunCMake/find_package/FindRegistryView.cmake
  64. 1 0
      Tests/RunCMake/find_package/REGISTRY_VIEW-no-view-result.txt
  65. 4 0
      Tests/RunCMake/find_package/REGISTRY_VIEW-no-view-stderr.txt
  66. 2 0
      Tests/RunCMake/find_package/REGISTRY_VIEW-no-view.cmake
  67. 16 0
      Tests/RunCMake/find_package/REGISTRY_VIEW-propagated.cmake
  68. 1 0
      Tests/RunCMake/find_package/REGISTRY_VIEW-wrong-view-result.txt
  69. 4 0
      Tests/RunCMake/find_package/REGISTRY_VIEW-wrong-view-stderr.txt
  70. 2 0
      Tests/RunCMake/find_package/REGISTRY_VIEW-wrong-view.cmake
  71. 216 0
      Tests/RunCMake/find_package/Registry-query.cmake
  72. 30 0
      Tests/RunCMake/find_package/RunCMakeTest.cmake
  73. 4 0
      Tests/RunCMake/find_package/default.32bit/RegistryView32Config.cmake
  74. 4 0
      Tests/RunCMake/find_package/default.32bit/RegistryViewConfig.cmake
  75. 4 0
      Tests/RunCMake/find_package/default.64bit/RegistryView64Config.cmake
  76. 4 0
      Tests/RunCMake/find_package/default.64bit/RegistryViewConfig.cmake
  77. 二进制
      Tests/RunCMake/find_package/registry_host32bit.reg
  78. 二进制
      Tests/RunCMake/find_package/registry_host64bit.reg
  79. 0 0
      Tests/RunCMake/find_path/32bit/file.txt
  80. 0 0
      Tests/RunCMake/find_path/32bit/file32bit.txt
  81. 0 0
      Tests/RunCMake/find_path/64bit/file.txt
  82. 0 0
      Tests/RunCMake/find_path/64bit/file64bit.txt
  83. 1 0
      Tests/RunCMake/find_path/REGISTRY_VIEW-no-view-result.txt
  84. 4 0
      Tests/RunCMake/find_path/REGISTRY_VIEW-no-view-stderr.txt
  85. 2 0
      Tests/RunCMake/find_path/REGISTRY_VIEW-no-view.cmake
  86. 1 0
      Tests/RunCMake/find_path/REGISTRY_VIEW-wrong-view-result.txt
  87. 4 0
      Tests/RunCMake/find_path/REGISTRY_VIEW-wrong-view-stderr.txt
  88. 2 0
      Tests/RunCMake/find_path/REGISTRY_VIEW-wrong-view.cmake
  89. 218 0
      Tests/RunCMake/find_path/Registry-query.cmake
  90. 28 0
      Tests/RunCMake/find_path/RunCMakeTest.cmake
  91. 0 0
      Tests/RunCMake/find_path/default.32bit/file.txt
  92. 0 0
      Tests/RunCMake/find_path/default.32bit/file32bit.txt
  93. 0 0
      Tests/RunCMake/find_path/default.64bit/file.txt
  94. 0 0
      Tests/RunCMake/find_path/default.64bit/file64bit.txt
  95. 二进制
      Tests/RunCMake/find_path/registry_host32bit.reg
  96. 二进制
      Tests/RunCMake/find_path/registry_host64bit.reg
  97. 0 0
      Tests/RunCMake/find_program/32bit/file.exe
  98. 0 0
      Tests/RunCMake/find_program/32bit/file32bit.exe
  99. 0 0
      Tests/RunCMake/find_program/64bit/file.exe
  100. 0 0
      Tests/RunCMake/find_program/64bit/file64bit.exe

+ 13 - 0
Help/command/FIND_XXX.txt

@@ -13,6 +13,7 @@ The general signature is:
              name | |NAMES|
              [HINTS [path | ENV var]... ]
              [PATHS [path | ENV var]... ]
+             [REGISTRY_VIEW (64|32|64_32|32_64|HOST|TARGET|BOTH)]
              [PATH_SUFFIXES suffix1 [suffix2 ...]]
              [DOC "cache documentation string"]
              [NO_CACHE]
@@ -51,6 +52,18 @@ Options include:
   The ``ENV var`` sub-option reads paths from a system environment
   variable.
 
+  .. versionchanged:: 3.24
+    On ``Windows`` platform, it is possible to include registry queries as part
+    of the directories. Such specifications will be ignored on all other
+    platforms.
+
+  .. include:: FIND_XXX_REGISTRY_QUERY.txt
+
+``REGISTRY_VIEW``
+  .. versionadded:: 3.24
+
+  .. include:: FIND_XXX_REGISTRY_VIEW.txt
+
 ``PATH_SUFFIXES``
   Specify additional subdirectories to check below each directory
   location otherwise considered.

+ 43 - 0
Help/command/FIND_XXX_REGISTRY_QUERY.txt

@@ -0,0 +1,43 @@
+The formal syntax, as specified using
+`BNF <https://en.wikipedia.org/wiki/Backus%E2%80%93Naur_form>`_ notation with
+the regular extensions, for registry query is the following:
+
+.. parsed-literal::
+
+  registry_query  ::= '[' `sep_definition`_? `root_key`_
+                      ((`key_separator`_ `sub_key`_)? (`value_separator`_ `value_name`_)?)? ']'
+  _`sep_definition`  ::= '{' `value_separator`_ '}'
+  _`root_key`        ::= 'HKLM' | 'HKEY_LOCAL_MACHINE' | 'HKCU' | 'HKEY_CURRENT_USER' |
+                      'HKCR' | 'HKEY_CLASSES_ROOT' | 'HKCC' | 'HKEY_CURRENT_CONFIG' |
+                      'HKU' | 'HKEY_USERS'
+  _`sub_key`         ::= `element`_ (`key_separator`_ `element`_)*
+  _`key_separator`   ::= '/' | '\\'
+  _`value_separator` ::= `element`_ | ';'
+  _`value_name`      ::= `element`_ | '(default)'
+  _`element`         ::= `character`_\+
+  _`character`       ::= <any character except `key_separator`_ and `value_separator`_>
+
+The `sep_definition`_ optional item offers the possibility to specify the
+string used to separate the `sub_key`_ from the `value_name`_ item. If
+not specified, the character ``;`` is used.
+
+.. parsed-literal::
+
+  # example using default separator
+  |FIND_XXX| (... **PATHS** "/root/[HKLM/Stuff;InstallDir]/lib[HKLM\\\\Stuff;Architecture]")
+
+  # example using different specified separators
+  |FIND_XXX| (... **HINTS** "/root/[{|}HKCU/Stuff|InstallDir]/lib[{@@}HKCU\\\\Stuff@@Architecture]")
+
+If the `value_name`_ item is not specified or has the special name
+``(default)``, the content of the default value, if any, will be returned. The
+supported types for the `value_name`_ are:
+
+* ``REG_SZ``.
+* ``REG_EXPAND_SZ``. The returned data is expanded.
+* ``REG_DWORD``.
+* ``REG_QWORD``.
+
+When the registry query failed, typically because the key does not exist or
+the data type is not supported, the string ``/REGISTRY-NOTFOUND`` is substituted
+to the ``[]`` query expression.

+ 41 - 0
Help/command/FIND_XXX_REGISTRY_VIEW.txt

@@ -0,0 +1,41 @@
+Specify which registry views must be queried. This option is only meaningful
+on ``Windows`` platform and will be ignored on other ones. When not
+specified, |FIND_XXX_REGISTRY_VIEW_DEFAULT| view is used when :policy:`CMP0134`
+policy is ``NEW``. Refer to :policy:`CMP0134` policy for default view when
+policy is ``OLD`` or undefined.
+
+``64``
+  Query the 64bit registry. On ``32bit Windows``, returns always the string
+  ``/REGISTRY-NOTFOUND``.
+
+``32``
+  Query the 32bit registry.
+
+``64_32``
+  Query both views (``64`` and ``32``) and generate a path for each.
+
+``32_64``
+  Query both views (``32`` and ``64``) and generate a path for each.
+
+``HOST``
+  Query the registry matching the architecture of the host: ``64`` on ``64bit
+  Windows`` and ``32`` on ``32bit Windows``.
+
+``TARGET``
+  Query the registry matching the architecture specified by
+  :variable:`CMAKE_SIZEOF_VOID_P` variable. If not defined, fallback to
+  ``HOST`` view.
+
+``BOTH``
+  Query both views (``32`` and ``64``). The order depends of the following
+  rules: If :variable:`CMAKE_SIZEOF_VOID_P` variable is defined. Use the
+  following view depending of the content of this variable:
+
+  * ``8``: ``64_32``
+  * ``4``: ``32_64``
+
+  If :variable:`CMAKE_SIZEOF_VOID_P` variable is not defined, rely on
+  architecture of the host:
+
+  * ``64bit``: ``64_32``
+  * ``32bit``: ``32``

+ 2 - 2
Help/command/add_custom_command.rst

@@ -288,12 +288,12 @@ The options are:
 
   .. productionlist:: depfile
     depfile: `rule`*
-    rule: `targets` (`:` (`separator` `dependencies`?)?)? `eol`
+    rule: `targets` (':' (`separator` `dependencies`?)?)? `eol`
     targets: `target` (`separator` `target`)* `separator`*
     target: `pathname`
     dependencies: `dependency` (`separator` `dependency`)* `separator`*
     dependency: `pathname`
-    separator: (space | line_continue)+
+    separator: (`space` | `line_continue`)+
     line_continue: '\' `eol`
     space: ' ' | '\t'
     pathname: `character`+

+ 2 - 0
Help/command/find_file.rst

@@ -8,6 +8,8 @@ find_file
 .. |prefix_XXX_SUBDIR| replace:: ``<prefix>/include``
 .. |entry_XXX_SUBDIR| replace:: ``<entry>/include``
 
+.. |FIND_XXX_REGISTRY_VIEW_DEFAULT| replace:: ``TARGET``
+
 .. |FIND_PACKAGE_ROOT_PREFIX_PATH_XXX| replace::
    ``<prefix>/include/<arch>`` if :variable:`CMAKE_LIBRARY_ARCHITECTURE`
    is set, and |FIND_PACKAGE_ROOT_PREFIX_PATH_XXX_SUBDIR|

+ 2 - 0
Help/command/find_library.rst

@@ -8,6 +8,8 @@ find_library
 .. |prefix_XXX_SUBDIR| replace:: ``<prefix>/lib``
 .. |entry_XXX_SUBDIR| replace:: ``<entry>/lib``
 
+.. |FIND_XXX_REGISTRY_VIEW_DEFAULT| replace:: ``TARGET``
+
 .. |FIND_PACKAGE_ROOT_PREFIX_PATH_XXX| replace::
    ``<prefix>/lib/<arch>`` if :variable:`CMAKE_LIBRARY_ARCHITECTURE` is set,
    and |FIND_PACKAGE_ROOT_PREFIX_PATH_XXX_SUBDIR|

+ 31 - 7
Help/command/find_package.rst

@@ -1,6 +1,12 @@
 find_package
 ------------
 
+.. |FIND_XXX| replace:: find_package
+.. |FIND_ARGS_XXX| replace:: <PackageName>
+.. |FIND_XXX_REGISTRY_VIEW_DEFAULT| replace:: ``TARGET``
+.. |CMAKE_FIND_ROOT_PATH_MODE_XXX| replace::
+   :variable:`CMAKE_FIND_ROOT_PATH_MODE_PACKAGE`
+
 .. only:: html
 
    .. contents::
@@ -74,11 +80,12 @@ sections on this page.
 Basic Signature
 ^^^^^^^^^^^^^^^
 
-.. code-block:: cmake
+.. parsed-literal::
 
   find_package(<PackageName> [version] [EXACT] [QUIET] [MODULE]
                [REQUIRED] [[COMPONENTS] [components...]]
                [OPTIONAL_COMPONENTS components...]
+               [REGISTRY_VIEW  (64|32|64_32|32_64|HOST|TARGET|BOTH)]
                [NO_POLICY_SCOPE]
                [GLOBAL])
 
@@ -116,6 +123,12 @@ define what occurs in such cases.  Common arrangements include assuming it
 should find all components, no components or some well-defined subset of the
 available components.
 
+.. versionadded:: 3.24
+  The ``REGISTRY_VIEW`` keyword enables to specify which registry views must be
+  queried. This keyword is only meaningful on ``Windows`` platform and will be
+  ignored on all other ones. Formally, it is up to the target package how to
+  interpret the registry view information given to it.
+
 Specifying the ``GLOBAL`` keyword will promote all imported targets to
 a global scope in the importing project. Alternatively this functionality
 can be enabled by setting the variable
@@ -155,7 +168,7 @@ of the ``NO_POLICY_SCOPE`` option.
 Full Signature
 ^^^^^^^^^^^^^^
 
-.. code-block:: cmake
+.. parsed-literal::
 
   find_package(<PackageName> [version] [EXACT] [QUIET]
                [REQUIRED] [[COMPONENTS] [components...]]
@@ -167,6 +180,7 @@ Full Signature
                [CONFIGS config1 [config2 ...]]
                [HINTS path1 [path2 ... ]]
                [PATHS path1 [path2 ... ]]
+               [REGISTRY_VIEW  (64|32|64_32|32_64|HOST|TARGET|BOTH)]
                [PATH_SUFFIXES suffix1 [suffix2 ...]]
                [NO_DEFAULT_PATH]
                [NO_PACKAGE_ROOT_PATH]
@@ -272,6 +286,19 @@ that order).
   if the :prop_gbl:`FIND_LIBRARY_USE_LIBX32_PATHS` property is set to ``TRUE``.
 * The ``lib`` path is always searched.
 
+.. versionchanged:: 3.24
+  On ``Windows`` platform, it is possible to include registry queries as part
+  of the directories specified through ``HINTS`` and ``PATHS`` keywords. Such
+  specifications will be ignored on all other platforms.
+
+.. include:: FIND_XXX_REGISTRY_QUERY.txt
+
+.. versionadded:: 3.24
+  ``REGISTRY_VIEW`` can be specified to manage ``Windows`` registry queries
+  specified as part of ``PATHS`` and ``HINTS``.
+
+.. include:: FIND_XXX_REGISTRY_VIEW.txt
+
 If ``PATH_SUFFIXES`` is specified, the suffixes are appended to each
 (``W``) or (``U``) directory entry one-by-one.
 
@@ -382,11 +409,6 @@ of the above locations to be ignored.
    Added the ``CMAKE_FIND_USE_<CATEGORY>`` variables to globally disable
    various search locations.
 
-.. |FIND_XXX| replace:: find_package
-.. |FIND_ARGS_XXX| replace:: <PackageName>
-.. |CMAKE_FIND_ROOT_PATH_MODE_XXX| replace::
-   :variable:`CMAKE_FIND_ROOT_PATH_MODE_PACKAGE`
-
 .. include:: FIND_XXX_ROOT.txt
 .. include:: FIND_XXX_ORDER.txt
 
@@ -557,6 +579,8 @@ restores their original state before returning):
   True if ``REQUIRED`` option was given
 ``<PackageName>_FIND_QUIETLY``
   True if ``QUIET`` option was given
+``<PackageName>_FIND_REGISTRY_VIEW``
+  The requested view if ``REGISTRY_VIEW`` option was given
 ``<PackageName>_FIND_VERSION``
   Full requested version string
 ``<PackageName>_FIND_VERSION_MAJOR``

+ 2 - 0
Help/command/find_path.rst

@@ -8,6 +8,8 @@ find_path
 .. |prefix_XXX_SUBDIR| replace:: ``<prefix>/include``
 .. |entry_XXX_SUBDIR| replace:: ``<entry>/include``
 
+.. |FIND_XXX_REGISTRY_VIEW_DEFAULT| replace:: ``TARGET``
+
 .. |FIND_PACKAGE_ROOT_PREFIX_PATH_XXX| replace::
    ``<prefix>/include/<arch>`` if :variable:`CMAKE_LIBRARY_ARCHITECTURE`
    is set, and |FIND_PACKAGE_ROOT_PREFIX_PATH_XXX_SUBDIR|

+ 2 - 0
Help/command/find_program.rst

@@ -8,6 +8,8 @@ find_program
 .. |prefix_XXX_SUBDIR| replace:: ``<prefix>/[s]bin``
 .. |entry_XXX_SUBDIR| replace:: ``<entry>/[s]bin``
 
+.. |FIND_XXX_REGISTRY_VIEW_DEFAULT| replace:: ``BOTH``
+
 .. |FIND_PACKAGE_ROOT_PREFIX_PATH_XXX| replace::
    |FIND_PACKAGE_ROOT_PREFIX_PATH_XXX_SUBDIR|
 .. |CMAKE_PREFIX_PATH_XXX| replace::

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

@@ -58,6 +58,7 @@ Policies Introduced by CMake 3.24
 .. toctree::
    :maxdepth: 1
 
+   CMP0134: Fallback to \"HOST\" Windows registry view when \"TARGET\" view is not usable. </policy/CMP0134>
    CMP0133: The CPack module disables SLA by default in the CPack DragNDrop Generator. </policy/CMP0133>
    CMP0132: Do not set compiler environment variables on first run. </policy/CMP0132>
    CMP0131: LINK_LIBRARIES supports the LINK_ONLY generator expression. </policy/CMP0131>

+ 39 - 0
Help/policy/CMP0134.rst

@@ -0,0 +1,39 @@
+CMP0134
+-------
+
+.. versionadded:: 3.24
+
+The default registry view is ``TARGET`` for the :command:`find_file`,
+:command:`find_path`, :command:`find_library`, and :command:`find_package`
+commands and ``BOTH`` for the :command:`find_program` command.
+
+The default registry views in CMake 3.23 and below are selected using the
+following rules:
+
+* if :variable:`CMAKE_SIZEOF_VOID_P` has value ``8``:
+
+  * Use view ``64`` for all ``find_*`` commands except :command:`find_program`
+    command.
+  * Use view ``64_32`` for :command:`find_program` command.
+
+* if :variable:`CMAKE_SIZEOF_VOID_P` has value ``4`` or is undefined:
+
+  * Use view ``32`` for all ``find_*`` commands except :command:`find_program`
+    command.
+  * Use view ``32_64`` for :command:`find_program` command.
+
+The ``OLD`` behavior for this policy is to use registry views ``64`` and
+``64_32`` or ``32_64`` and ``32`` as default, depending of
+:variable:`CMAKE_SIZEOF_VOID_P` variable value.
+The ``NEW`` behavior for this policy is to use registry views ``TARGET`` and
+``BOTH`` as default.
+
+This policy was introduced in CMake version 3.24.  Use the
+:command:`cmake_policy` command to set this policy to ``OLD`` or ``NEW``
+explicitly. Unlike many policies, CMake version |release| does *not* warn
+by default when this policy is not set and simply uses ``OLD`` behavior.
+See documentation of the
+:variable:`CMAKE_POLICY_WARNING_CMP0133 <CMAKE_POLICY_WARNING_CMP<NNNN>>`
+variable to control the warning.
+
+.. include:: DEPRECATED.txt

+ 6 - 0
Help/release/dev/find_item-query-windows-registry.rst

@@ -0,0 +1,6 @@
+find_item-query-windows-registry.rst
+------------------------------------
+
+* :command:`find_file`, :command:`find_path`, :command:`find_library`,
+  :command:`find_program`, and :command:`find_package` commands gain the
+  capability to specify which registry views must be queried.

+ 15 - 0
Source/cmFindBase.cxx

@@ -7,6 +7,7 @@
 #include <map>
 #include <utility>
 
+#include <cm/optional>
 #include <cmext/algorithm>
 
 #include "cmCMakePath.h"
@@ -20,6 +21,7 @@
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmValue.h"
+#include "cmWindowsRegistry.h"
 #include "cmake.h"
 
 class cmExecutionStatus;
@@ -123,6 +125,19 @@ bool cmFindBase::ParseArguments(std::vector<std::string> const& argsIn)
       doing = DoingNone;
       this->Required = true;
       newStyle = true;
+    } else if (args[j] == "REGISTRY_VIEW") {
+      if (++j == args.size()) {
+        this->SetError("missing required argument for \"REGISTRY_VIEW\"");
+        return false;
+      }
+      auto view = cmWindowsRegistry::ToView(args[j]);
+      if (view) {
+        this->RegistryView = *view;
+      } else {
+        this->SetError(
+          cmStrCat("given invalid value for \"REGISTRY_VIEW\": ", args[j]));
+        return false;
+      }
     } else if (this->CheckCommonArgument(args[j])) {
       doing = DoingNone;
     } else {

+ 12 - 0
Source/cmFindCommon.cxx

@@ -11,6 +11,7 @@
 #include "cmExecutionStatus.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
+#include "cmPolicies.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmValue.h"
@@ -58,6 +59,17 @@ cmFindCommon::cmFindCommon(cmExecutionStatus& status)
   this->InitializeSearchPathGroups();
 
   this->DebugMode = false;
+
+  // Windows Registry views
+  // When policy CMP0134 is not NEW, rely on previous behavior:
+  if (this->Makefile->GetPolicyStatus(cmPolicies::CMP0134) !=
+      cmPolicies::NEW) {
+    if (this->Makefile->GetDefinition("CMAKE_SIZEOF_VOID_P") == "8") {
+      this->RegistryView = cmWindowsRegistry::View::Reg64;
+    } else {
+      this->RegistryView = cmWindowsRegistry::View::Reg32;
+    }
+  }
 }
 
 void cmFindCommon::SetError(std::string const& e)

+ 2 - 0
Source/cmFindCommon.h

@@ -11,6 +11,7 @@
 
 #include "cmPathLabel.h"
 #include "cmSearchPath.h"
+#include "cmWindowsRegistry.h"
 
 class cmExecutionStatus;
 class cmMakefile;
@@ -131,6 +132,7 @@ protected:
   bool NoSystemEnvironmentPath;
   bool NoCMakeSystemPath;
   bool NoCMakeInstallPath;
+  cmWindowsRegistry::View RegistryView = cmWindowsRegistry::View::Target;
 
   std::vector<std::string> SearchPathSuffixes;
 

+ 21 - 0
Source/cmFindPackageCommand.cxx

@@ -13,6 +13,7 @@
 #include <utility>
 
 #include <cm/memory>
+#include <cm/optional>
 #include <cmext/string_view>
 
 #include "cmsys/Directory.hxx"
@@ -33,6 +34,7 @@
 #include "cmSystemTools.h"
 #include "cmValue.h"
 #include "cmVersion.h"
+#include "cmWindowsRegistry.h"
 
 #if defined(__HAIKU__)
 #  include <FindDirectory.h>
@@ -317,6 +319,20 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
       // Ignore legacy option.
       configArgs.insert(i);
       doing = DoingNone;
+    } else if (args[i] == "REGISTRY_VIEW") {
+      if (++i == args.size()) {
+        this->SetError("missing required argument for \"REGISTRY_VIEW\"");
+        return false;
+      }
+      auto view = cmWindowsRegistry::ToView(args[i]);
+      if (view) {
+        this->RegistryView = *view;
+        this->RegistryViewDefined = true;
+      } else {
+        this->SetError(
+          cmStrCat("given invalid value for \"REGISTRY_VIEW\": ", args[i]));
+        return false;
+      }
     } else if (this->CheckCommonArgument(args[i])) {
       configArgs.insert(i);
       doing = DoingNone;
@@ -767,6 +783,11 @@ void cmFindPackageCommand::SetModuleVariables(const std::string& components)
     id = cmStrCat(this->Name, "_FIND_VERSION_RANGE_MAX");
     this->AddFindDefinition(id, this->VersionRangeMax);
   }
+
+  if (this->RegistryViewDefined) {
+    this->AddFindDefinition(cmStrCat(this->Name, "_FIND_REGISTRY_VIEW"),
+                            cmWindowsRegistry::FromView(this->RegistryView));
+  }
 }
 
 void cmFindPackageCommand::AddFindDefinition(const std::string& var,

+ 1 - 0
Source/cmFindPackageCommand.h

@@ -200,6 +200,7 @@ private:
   bool UseRealPath = false;
   bool PolicyScope = true;
   bool GlobalScope = false;
+  bool RegistryViewDefined = false;
   std::string LibraryArchitecture;
   std::vector<std::string> Names;
   std::vector<std::string> Configs;

+ 14 - 0
Source/cmFindProgramCommand.cxx

@@ -12,6 +12,8 @@
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
+#include "cmWindowsRegistry.h"
 
 class cmExecutionStatus;
 
@@ -172,6 +174,18 @@ cmFindProgramCommand::cmFindProgramCommand(cmExecutionStatus& status)
   this->NamesPerDirAllowed = true;
   this->VariableDocumentation = "Path to a program.";
   this->VariableType = cmStateEnums::FILEPATH;
+  // Windows Registry views
+  // When policy CMP0134 is not NEW, rely on previous behavior:
+  if (this->Makefile->GetPolicyStatus(cmPolicies::CMP0134) !=
+      cmPolicies::NEW) {
+    if (this->Makefile->GetDefinition("CMAKE_SIZEOF_VOID_P") == "8") {
+      this->RegistryView = cmWindowsRegistry::View::Reg64_32;
+    } else {
+      this->RegistryView = cmWindowsRegistry::View::Reg32_64;
+    }
+  } else {
+    this->RegistryView = cmWindowsRegistry::View::Both;
+  }
 }
 
 // cmFindProgramCommand

+ 4 - 0
Source/cmPolicies.h

@@ -400,6 +400,10 @@ class cmMakefile;
   SELECT(POLICY, CMP0133,                                                     \
          "The CPack module disables SLA by default in the CPack DragNDrop "   \
          "Generator.",                                                        \
+         3, 24, 0, cmPolicies::WARN)                                          \
+  SELECT(POLICY, CMP0134,                                                     \
+         "Fallback to \"HOST\" Windows registry view when \"TARGET\" view "   \
+         "is not usable.",                                                    \
          3, 24, 0, cmPolicies::WARN)
 
 #define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1)

+ 10 - 20
Source/cmSearchPath.cxx

@@ -6,11 +6,14 @@
 #include <cassert>
 #include <utility>
 
+#include <cm/optional>
+
 #include "cmFindCommon.h"
 #include "cmMakefile.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmValue.h"
+#include "cmWindowsRegistry.h"
 
 cmSearchPath::cmSearchPath(cmFindCommon* findCmd)
   : FC(findCmd)
@@ -46,26 +49,13 @@ void cmSearchPath::AddUserPath(const std::string& path)
 
   std::vector<std::string> outPaths;
 
-  // 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 (this->FC->Makefile->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, outPaths);
-
-  // Executables can be either 32-bit or 64-bit, so expand using the
-  // alternative view.
-  if (expanded != path && this->FC->CMakePathName == "PROGRAM") {
-    expanded = path;
-    cmSystemTools::ExpandRegistryValues(expanded, other_view);
-    cmSystemTools::GlobDirs(expanded, outPaths);
+  cmWindowsRegistry registry(*this->FC->Makefile,
+                             cmWindowsRegistry::SimpleTypes);
+  auto expandedPaths = registry.ExpandExpression(path, this->FC->RegistryView);
+  if (expandedPaths) {
+    for (const auto& expandedPath : expandedPaths.value()) {
+      cmSystemTools::GlobDirs(expandedPath, outPaths);
+    }
   }
 
   // Process them all from the current directory

+ 255 - 26
Source/cmWindowsRegistry.cxx

@@ -1,33 +1,61 @@
 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
    file Copyright.txt or https://cmake.org/licensing for details.  */
+#include "cmConfigure.h" // IWYU pragma: keep
 
 #include "cmWindowsRegistry.h"
 
+#include <cctype>
+#include <cstddef>
+#include <functional>
+#include <type_traits>
 #include <unordered_map>
+#include <utility>
+
+#include <cmext/string_view>
+
+#include "cmsys/RegularExpression.hxx"
 
 #if defined(_WIN32) && !defined(__CYGWIN__)
 #  include <algorithm>
-#  include <cstdint>
+#  include <cstring>
 #  include <exception>
 #  include <iterator>
-#  include <utility>
 #  include <vector>
 
 #  include <cm/memory>
-#  include <cmext/string_view>
 
 #  include <windows.h>
 
-#  include "cmsys/Encoding.hxx"
-#  include "cmsys/SystemTools.hxx"
-
 #  include "cmMakefile.h"
 #  include "cmStringAlgorithms.h"
 #  include "cmValue.h"
 #endif
 
-#if defined(_WIN32) && !defined(__CYGWIN__)
 namespace {
+//  Case-independent string comparison
+int Strucmp(cm::string_view l, cm::string_view r)
+{
+  if (l.empty() && r.empty()) {
+    return 0;
+  }
+  if (l.empty() || r.empty()) {
+    return static_cast<int>(l.size() - r.size());
+  }
+
+  int lc;
+  int rc;
+  cm::string_view::size_type li = 0;
+  cm::string_view::size_type ri = 0;
+
+  do {
+    lc = std::tolower(l[li++]);
+    rc = std::tolower(r[ri++]);
+  } while (lc == rc && li < l.size() && ri < r.size());
+
+  return lc == rc ? static_cast<int>(l.size() - r.size()) : lc - rc;
+}
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
 bool Is64BitWindows()
 {
 #  if defined(_WIN64)
@@ -40,6 +68,26 @@ bool Is64BitWindows()
 #  endif
 }
 
+// Helper to translate Windows registry value type to enum ValueType
+cm::optional<cmWindowsRegistry::ValueType> ToValueType(DWORD type)
+{
+  using ValueType = cmWindowsRegistry::ValueType;
+
+  static std::unordered_map<DWORD, ValueType> ValueTypes{
+    { REG_SZ, ValueType::Reg_SZ },
+    { REG_EXPAND_SZ, ValueType::Reg_EXPAND_SZ },
+    { REG_MULTI_SZ, ValueType::Reg_MULTI_SZ },
+    { REG_DWORD, ValueType::Reg_DWORD },
+    { REG_QWORD, ValueType::Reg_QWORD }
+  };
+
+  auto it = ValueTypes.find(type);
+
+  return it == ValueTypes.end()
+    ? cm::nullopt
+    : cm::optional<cmWindowsRegistry::ValueType>{ it->second };
+}
+
 // class registry_exception
 class registry_error : public std::exception
 {
@@ -61,6 +109,7 @@ class KeyHandler
 {
 public:
   using View = cmWindowsRegistry::View;
+  using ValueTypeSet = cmWindowsRegistry::ValueTypeSet;
 
   KeyHandler(HKEY hkey)
     : Handler(hkey)
@@ -68,9 +117,14 @@ public:
   }
   ~KeyHandler() { RegCloseKey(this->Handler); }
 
+  static KeyHandler OpenKey(cm::string_view rootKey, cm::string_view subKey,
+                            View view);
   static KeyHandler OpenKey(cm::string_view key, View view);
 
-  std::string ReadValue(cm::string_view name, cm::string_view separator);
+  std::string ReadValue(
+    cm::string_view name,
+    ValueTypeSet supportedTypes = cmWindowsRegistry::AllTypes,
+    cm::string_view separator = "\0"_s);
 
   std::vector<std::string> GetValueNames();
   std::vector<std::string> GetSubKeys();
@@ -83,16 +137,14 @@ private:
   HKEY Handler;
 };
 
-KeyHandler KeyHandler::OpenKey(cm::string_view key, View view)
+KeyHandler KeyHandler::OpenKey(cm::string_view rootKey, cm::string_view subKey,
+                               View view)
 {
   if (view == View::Reg64 && !Is64BitWindows()) {
     throw registry_error("No 64bit registry on Windows32.");
   }
 
-  auto start = key.find_first_of("\\/"_s);
-  auto rootKey = key.substr(0, start);
   HKEY hRootKey;
-
   if (rootKey == "HKCU"_s || rootKey == "HKEY_CURRENT_USER"_s) {
     hRootKey = HKEY_CURRENT_USER;
   } else if (rootKey == "HKLM"_s || rootKey == "HKEY_LOCAL_MACHINE"_s) {
@@ -106,12 +158,9 @@ KeyHandler KeyHandler::OpenKey(cm::string_view key, View view)
   } else {
     throw registry_error(cmStrCat(rootKey, ": invalid root key."));
   }
-  std::wstring subKey;
-  if (start != cm::string_view::npos) {
-    subKey = ToWide(key.substr(start + 1));
-  }
   // Update path format
-  std::replace(subKey.begin(), subKey.end(), L'/', L'\\');
+  auto key = ToWide(subKey);
+  std::replace(key.begin(), key.end(), L'/', L'\\');
 
   REGSAM options = KEY_READ;
   if (Is64BitWindows()) {
@@ -119,14 +168,25 @@ KeyHandler KeyHandler::OpenKey(cm::string_view key, View view)
   }
 
   HKEY hKey;
-  if (LSTATUS status = RegOpenKeyExW(hRootKey, subKey.c_str(), 0, options,
-                                     &hKey) != ERROR_SUCCESS) {
+  LSTATUS status;
+  if ((status = RegOpenKeyExW(hRootKey, key.c_str(), 0, options, &hKey)) !=
+      ERROR_SUCCESS) {
     throw registry_error(FormatSystemError(status));
   }
 
   return KeyHandler(hKey);
 }
 
+KeyHandler KeyHandler::OpenKey(cm::string_view key, View view)
+{
+  auto start = key.find_first_of("\\/"_s);
+
+  return OpenKey(key.substr(0, start),
+                 start == cm::string_view::npos ? cm::string_view{ ""_s }
+                                                : key.substr(start + 1),
+                 view);
+}
+
 std::string KeyHandler::FormatSystemError(LSTATUS status)
 {
   std::string formattedMessage{ "Windows Registry: unexpected error." };
@@ -208,6 +268,7 @@ std::string KeyHandler::ToNarrow(const wchar_t* wstr, int size)
 }
 
 std::string KeyHandler::ReadValue(cm::string_view name,
+                                  ValueTypeSet supportedTypes,
                                   cm::string_view separator)
 {
   LSTATUS status;
@@ -225,6 +286,12 @@ std::string KeyHandler::ReadValue(cm::string_view name,
                                  &type, data.get(), &size)) != ERROR_SUCCESS) {
     throw registry_error(this->FormatSystemError(status));
   }
+
+  auto valueType = ToValueType(type);
+  if (!valueType || !supportedTypes.contains(*valueType)) {
+    throw registry_error(cmStrCat(type, ": unsupported type."));
+  }
+
   switch (type) {
     case REG_SZ:
       return this->ToNarrow(reinterpret_cast<wchar_t*>(data.get()));
@@ -319,12 +386,109 @@ std::vector<std::string> KeyHandler::GetSubKeys()
 
   return subKeys;
 }
-}
 #endif
 
+// ExpressionParser: Helper to parse expression holding multiple
+// registry specifications
+class ExpressionParser
+{
+public:
+  ExpressionParser(cm::string_view expression)
+    : Expression(expression)
+    , Separator(";"_s)
+    , RegistryFormat{
+      "\\[({.+})?(HKCU|HKEY_CURRENT_USER|HKLM|HKEY_LOCAL_MACHINE|HKCR|HKEY_"
+      "CLASSES_"
+      "ROOT|HKCC|HKEY_CURRENT_CONFIG|HKU|HKEY_USERS)[/\\]?([^]]*)\\]"
+    }
+  {
+  }
+
+  bool Find()
+  {
+    // reset result members
+    this->RootKey = cm::string_view{};
+    this->SubKey = cm::string_view{};
+    this->ValueName = cm::string_view{};
+
+    auto result = this->RegistryFormat.find(this->Expression);
+
+    if (result) {
+      auto separator = cm::string_view{
+        this->Expression.data() + this->RegistryFormat.start(1),
+        this->RegistryFormat.end(1) - this->RegistryFormat.start(1)
+      };
+      if (separator.empty()) {
+        separator = this->Separator;
+      } else {
+        separator = separator.substr(1, separator.length() - 2);
+      }
+
+      this->RootKey = cm::string_view{
+        this->Expression.data() + this->RegistryFormat.start(2),
+        this->RegistryFormat.end(2) - this->RegistryFormat.start(2)
+      };
+      this->SubKey = cm::string_view{
+        this->Expression.data() + this->RegistryFormat.start(3),
+        this->RegistryFormat.end(3) - this->RegistryFormat.start(3)
+      };
+
+      auto pos = this->SubKey.find(separator);
+      if (pos != cm::string_view::npos) {
+        // split in ValueName and SubKey
+        this->ValueName = this->SubKey.substr(pos + separator.size());
+        if (Strucmp(this->ValueName, "(default)") == 0) {
+          // handle magic name for default value
+          this->ValueName = ""_s;
+        }
+        this->SubKey = this->SubKey.substr(0, pos);
+      } else {
+        this->ValueName = ""_s;
+      }
+    }
+    return result;
+  }
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+  void Replace(const std::string& value)
+  {
+    this->Expression.replace(
+      this->RegistryFormat.start(),
+      this->RegistryFormat.end() - this->RegistryFormat.start(), value);
+  }
+
+  cm::string_view GetRootKey() const { return this->RootKey; }
+
+  cm::string_view GetSubKey() const { return this->SubKey; }
+  cm::string_view GetValueName() const { return this->ValueName; }
+
+  const std::string& GetExpression() const { return this->Expression; }
+#endif
+
+private:
+  std::string Expression;
+  cm::string_view Separator;
+  cmsys::RegularExpression RegistryFormat;
+  cm::string_view RootKey;
+  cm::string_view SubKey;
+  cm::string_view ValueName;
+};
+}
+
 // class cmWindowsRegistry
-cmWindowsRegistry::cmWindowsRegistry(cmMakefile& makefile)
-#if !defined(_WIN32) || defined(__CYGWIN__)
+const cmWindowsRegistry::ValueTypeSet cmWindowsRegistry::SimpleTypes =
+  cmWindowsRegistry::ValueTypeSet{ cmWindowsRegistry::ValueType::Reg_SZ,
+                                   cmWindowsRegistry::ValueType::Reg_EXPAND_SZ,
+                                   cmWindowsRegistry::ValueType::Reg_DWORD,
+                                   cmWindowsRegistry::ValueType::Reg_QWORD };
+const cmWindowsRegistry::ValueTypeSet cmWindowsRegistry::AllTypes =
+  cmWindowsRegistry::SimpleTypes + cmWindowsRegistry::ValueType::Reg_MULTI_SZ;
+
+cmWindowsRegistry::cmWindowsRegistry(cmMakefile& makefile,
+                                     const ValueTypeSet& supportedTypes)
+#if defined(_WIN32) && !defined(__CYGWIN__)
+  : SupportedTypes(supportedTypes)
+#else
   : LastError("No Registry on this platform.")
 #endif
 {
@@ -334,6 +498,7 @@ cmWindowsRegistry::cmWindowsRegistry(cmMakefile& makefile)
   }
 #else
   (void)makefile;
+  (void)supportedTypes;
 #endif
 }
 
@@ -350,8 +515,22 @@ cm::optional<cmWindowsRegistry::View> cmWindowsRegistry::ToView(
 
   auto it = ViewDefinitions.find(name);
 
-  return it == ViewDefinitions.end() ? cm::nullopt
-                                     : cm::optional{ it->second };
+  return it == ViewDefinitions.end()
+    ? cm::nullopt
+    : cm::optional<cmWindowsRegistry::View>{ it->second };
+}
+
+// define hash structure required by std::unordered_map
+namespace std {
+template <>
+struct hash<cmWindowsRegistry::View>
+{
+  size_t operator()(cmWindowsRegistry::View const& v) const noexcept
+  {
+    return static_cast<
+      typename underlying_type<cmWindowsRegistry::View>::type>(v);
+  }
+};
 }
 
 cm::string_view cmWindowsRegistry::FromView(View view)
@@ -434,7 +613,7 @@ cm::optional<std::string> cmWindowsRegistry::ReadValue(
   // compute list of registry views
   auto views = this->ComputeViews(view);
 
-  if (cmsys::SystemTools::Strucmp(name.data(), "(default)") == 0) {
+  if (Strucmp(name, "(default)") == 0) {
     // handle magic name for default value
     name = ""_s;
   }
@@ -446,7 +625,7 @@ cm::optional<std::string> cmWindowsRegistry::ReadValue(
     try {
       this->LastError.clear();
       auto handler = KeyHandler::OpenKey(key, v);
-      return handler.ReadValue(name, separator);
+      return handler.ReadValue(name, this->SupportedTypes, separator);
     } catch (const registry_error& e) {
       this->LastError = e.what();
       continue;
@@ -539,3 +718,53 @@ cm::optional<std::vector<std::string>> cmWindowsRegistry::GetSubKeys(
 #endif
   return cm::nullopt;
 }
+
+cm::optional<std::vector<std::string>> cmWindowsRegistry::ExpandExpression(
+  cm::string_view expression, View view, cm::string_view separator)
+{
+#if defined(_WIN32) && !defined(__CYGWIN__)
+  static std::string NOTFOUND{ "/REGISTRY-NOTFOUND" };
+
+  this->LastError.clear();
+
+  // compute list of registry views
+  auto views = this->ComputeViews(view);
+  std::vector<std::string> result;
+
+  for (auto v : views) {
+    ExpressionParser parser(expression);
+
+    while (parser.Find()) {
+      try {
+        auto handler =
+          KeyHandler::OpenKey(parser.GetRootKey(), parser.GetSubKey(), v);
+        auto data = handler.ReadValue(parser.GetValueName(),
+                                      this->SupportedTypes, separator);
+        parser.Replace(data);
+      } catch (const registry_error& e) {
+        this->LastError = e.what();
+        parser.Replace(NOTFOUND);
+        continue;
+      }
+    }
+    result.emplace_back(parser.GetExpression());
+    if (expression == parser.GetExpression()) {
+      // there no substitutions, so can ignore other views
+      break;
+    }
+  }
+
+  return result;
+#else
+  (void)view;
+  (void)separator;
+
+  ExpressionParser parser(expression);
+  if (parser.Find()) {
+    // expression holds unsupported registry access
+    // so the expression cannot be used on this platform
+    return cm::nullopt;
+  }
+  return std::vector<std::string>{ std::string{ expression } };
+#endif
+}

+ 27 - 2
Source/cmWindowsRegistry.h

@@ -7,6 +7,7 @@
 
 #include <cm/optional>
 #include <cm/string_view>
+#include <cmext/enum_set>
 #include <cmext/string_view>
 
 class cmMakefile;
@@ -14,8 +15,6 @@ class cmMakefile;
 class cmWindowsRegistry
 {
 public:
-  cmWindowsRegistry(cmMakefile&);
-
   enum class View
   {
     Both,
@@ -27,6 +26,25 @@ public:
     Reg64
   };
 
+  // Registry supported types
+  enum class ValueType : std::uint8_t
+  {
+    Reg_SZ,
+    Reg_EXPAND_SZ,
+    Reg_MULTI_SZ,
+    Reg_DWORD,
+    Reg_QWORD
+  };
+  using ValueTypeSet = cm::enum_set<ValueType>;
+
+  // All types as defined by enum ValueType
+  static const ValueTypeSet AllTypes;
+  // same as AllTYpes but without type REG_MULTI_SZ
+  static const ValueTypeSet SimpleTypes;
+
+  cmWindowsRegistry(cmMakefile&,
+                    const ValueTypeSet& supportedTypes = AllTypes);
+
   // Helper routine to convert string to enum value
   static cm::optional<View> ToView(cm::string_view name);
   // Helper routine to convert enum to string
@@ -48,6 +66,12 @@ public:
   cm::optional<std::vector<std::string>> GetSubKeys(cm::string_view key,
                                                     View view = View::Both);
 
+  // Expand an expression which may contains multiple references
+  // to registry keys.
+  // Depending of the view specified, one or two expansions can be done.
+  cm::optional<std::vector<std::string>> ExpandExpression(
+    cm::string_view expression, View view, cm::string_view separator = "\0"_s);
+
   cm::string_view GetLastError() const;
 
 private:
@@ -55,6 +79,7 @@ private:
   std::vector<View> ComputeViews(View view);
 
   int TargetSize = 0;
+  ValueTypeSet SupportedTypes = AllTypes;
 #endif
   std::string LastError;
 };

+ 0 - 0
Tests/RunCMake/find_file/32bit/file.txt


+ 0 - 0
Tests/RunCMake/find_file/32bit/file32bit.txt


+ 0 - 0
Tests/RunCMake/find_file/64bit/file.txt


+ 0 - 0
Tests/RunCMake/find_file/64bit/file64bit.txt


+ 1 - 0
Tests/RunCMake/find_file/REGISTRY_VIEW-no-view-result.txt

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

+ 4 - 0
Tests/RunCMake/find_file/REGISTRY_VIEW-no-view-stderr.txt

@@ -0,0 +1,4 @@
+CMake Error at REGISTRY_VIEW-no-view.cmake:[0-9]+ \(find_file\):
+  find_file missing required argument for "REGISTRY_VIEW"
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)

+ 2 - 0
Tests/RunCMake/find_file/REGISTRY_VIEW-no-view.cmake

@@ -0,0 +1,2 @@
+
+find_file(result NAMES input.txt REGISTRY_VIEW)

+ 1 - 0
Tests/RunCMake/find_file/REGISTRY_VIEW-wrong-view-result.txt

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

+ 4 - 0
Tests/RunCMake/find_file/REGISTRY_VIEW-wrong-view-stderr.txt

@@ -0,0 +1,4 @@
+CMake Error at REGISTRY_VIEW-wrong-view.cmake:[0-9]+ \(find_file\):
+  find_file given invalid value for "REGISTRY_VIEW": WRONG_VIEW
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)

+ 2 - 0
Tests/RunCMake/find_file/REGISTRY_VIEW-wrong-view.cmake

@@ -0,0 +1,2 @@
+
+find_file(result NAMES input.txt REGISTRY_VIEW WRONG_VIEW)

+ 218 - 0
Tests/RunCMake/find_file/Registry-query.cmake

@@ -0,0 +1,218 @@
+
+# helper function for test validation
+function(CHECK query result expression)
+  cmake_language(EVAL CODE
+    "if (NOT (${expression}))
+       message(SEND_ERROR \"wrong value for query '${query}': '${result}'\")
+     endif()")
+endfunction()
+
+
+cmake_policy(SET CMP0134 NEW)
+
+# HKCU/Software/Classes/CLSID/CMake-Tests/find_file: Query default value
+set(FILE_DIR "[HKCU/Software/Classes/CLSID/CMake-Tests/find_file]")
+set(FILE_DIR2 "[HKCU/Software/Classes/CLSID/CMake-Tests/find_file;(default)]")
+
+unset(result)
+find_file(result NAMES file.txt PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REQUIRED NO_CACHE NO_DEFAULT_PATH)
+check("${FILE_DIR}" "${result}" "result MATCHES \"default.${ARCH}/file.txt$\"")
+
+# query value using special name should be identical to default value
+unset(result)
+find_file(result NAMES file.txt PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR2}" REQUIRED NO_CACHE NO_DEFAULT_PATH)
+check("${FILE_DIR2}" "${result}" "result MATCHES \"default.${ARCH}/file.txt$\"")
+
+unset(result)
+find_file(result NAMES file.txt PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW HOST REQUIRED NO_CACHE NO_DEFAULT_PATH)
+check("${FILE_DIR}" "${result}" "result MATCHES \"default.${ARCH}/file.txt$\"")
+# VIEW TARGET should have same value as VIEW HOST
+unset(result2)
+find_file(result2 NAMES file.txt PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW TARGET REQUIRED NO_CACHE NO_DEFAULT_PATH)
+check("${FILE_DIR}" "${result2}" "result2 STREQUAL result")
+
+if (ARCH STREQUAL "64bit")
+
+  unset(result)
+  find_file(result NAMES file.txt PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 64 REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"default.64bit/file.txt$\"")
+
+  unset(result)
+  find_file(result NAMES file.txt PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 32 REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"default.32bit/file.txt$\"")
+
+  unset(result)
+  find_file(result NAMES file.txt PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 64_32 REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"default.64bit/file.txt$\"")
+  unset(result)
+
+  find_file(result NAMES file.txt PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 32_64 REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"default.32bit/file.txt$\"")
+
+  # check the second view is taken into account
+  unset(result)
+  find_file(result NAMES file32bit.txt PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 64_32 REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"default.32bit/file32bit.txt$\"")
+
+  unset(result)
+  find_file(result NAMES file64bit.txt PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 32_64 REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"default.64bit/file64bit.txt$\"")
+
+  # check the both views are taken into account
+  unset(result)
+  find_file(result NAMES file32bit.txt PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW BOTH REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"default.32bit/file32bit.txt$\"")
+
+  unset(result)
+  find_file(result NAMES file64bit.txt PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW BOTH REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"default.64bit/file64bit.txt$\"")
+
+else() # 32bit
+
+  # no 64bit registry: file not found
+  unset(result)
+  find_file(result NAMES file.txt PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 64 NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"result-NOTFOUND$\"")
+
+  unset(result)
+  find_file(result NAMES file.txt PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 32 REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"default.32bit/file.txt$\"")
+
+  # views 64_32 and 32_64 give same result
+  unset(result)
+  find_file(result NAMES file.txt PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 64_32 REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"default.32bit/file.txt$\"")
+
+  unset(result)
+  find_file(result NAMES file.txt PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 32_64 REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"default.32bit/file.txt$\"")
+
+  # check the both views are usable on 32bit platforms
+  unset(result)
+  find_file(result NAMES file.txt PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW BOTH REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"default.32bit/file.txt$\"")
+
+endif()
+
+# HKCU/Software/Classes/CLSID/CMake-Tests/find_file: Query specific value
+set(FILE_DIR "[{|}HKCU/Software/Classes/CLSID/CMake-Tests/find_file|FILE_DIR]")
+set(FILE_DIR2 "[HKCU\\Software\\Classes\\CLSID\\CMake-Tests\\find_file;FILE_DIR]")
+
+unset(result)
+find_file(result NAMES file.txt HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REQUIRED NO_CACHE NO_DEFAULT_PATH)
+check("${FILE_DIR}" "${result}" "result MATCHES \"/${ARCH}/file.txt$\"")
+
+# query value using special name should be identical to default value
+unset(result)
+find_file(result NAMES file.txt HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR2}" REQUIRED NO_CACHE NO_DEFAULT_PATH)
+check("${FILE_DIR2}" "${result}" "result MATCHES \"/${ARCH}/file.txt$\"")
+
+unset(result)
+find_file(result NAMES file.txt HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW HOST REQUIRED NO_CACHE NO_DEFAULT_PATH)
+check("${FILE_DIR}" "${result}" "result MATCHES \"/${ARCH}/file.txt$\"")
+# VIEW TARGET should have same value as VIEW HOST
+unset(result2)
+find_file(result2 NAMES file.txt HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW TARGET REQUIRED NO_CACHE NO_DEFAULT_PATH)
+check("${FILE_DIR}" "${result2}" "result2 STREQUAL result")
+
+if (ARCH STREQUAL "64bit")
+
+  unset(result)
+  find_file(result NAMES file.txt HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 64 REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"/64bit/file.txt$\"")
+
+  unset(result)
+  find_file(result NAMES file.txt HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 32 REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"/32bit/file.txt$\"")
+
+  unset(result)
+  find_file(result NAMES file.txt HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 64_32 REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"/64bit/file.txt$\"")
+  unset(result)
+
+  find_file(result NAMES file.txt HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 32_64 REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"/32bit/file.txt$\"")
+
+  # check the second view is taken into account
+  unset(result)
+  find_file(result NAMES file32bit.txt HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 64_32 REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"/32bit/file32bit.txt$\"")
+
+  unset(result)
+  find_file(result NAMES file64bit.txt HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 32_64 NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"/64bit/file64bit.txt$\"")
+
+  # check the both views are taken into account
+  unset(result)
+  find_file(result NAMES file32bit.txt HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW BOTH REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"/32bit/file32bit.txt$\"")
+
+  unset(result)
+  find_file(result NAMES file64bit.txt HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW BOTH REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"/64bit/file64bit.txt$\"")
+
+else() # 32bit
+
+  # no 64bit registry: file not found
+  unset(result)
+  find_file(result NAMES file.txt HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 64 NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"result-NOTFOUND$\"")
+
+  unset(result)
+  find_file(result NAMES file.txt HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 32 REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"/32bit/file.txt$\"")
+
+  unset(result)
+  find_file(result NAMES file.txt HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 64_32 REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"/32bit/file.txt$\"")
+
+  unset(result)
+  find_file(result NAMES file.txt HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 32_64 REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"/32bit/file.txt$\"")
+
+  # check the both views are taken into account
+  unset(result)
+  find_file(result NAMES file.txt HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW BOTH REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"/32bit/file.txt$\"")
+
+endif()
+
+if (ARCH STREQUAL "64bit")
+
+  # Check influence of variable CMAKE_SIZEOF_VOID_P
+  set(CMAKE_SIZEOF_VOID_P 8)
+  unset(result)
+  find_file(result NAMES file.txt PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW TARGET REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"/64bit/file.txt$\"")
+
+  unset(result)
+  find_file(result NAMES file.txt PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW HOST REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"/${ARCH}/file.txt$\"")
+
+
+  set(CMAKE_SIZEOF_VOID_P 4)
+  unset(result)
+  find_file(result NAMES file.txt PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW TARGET REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"/32bit/file.txt$\"")
+
+  unset(result)
+  find_file(result NAMES file.txt PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW HOST REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"/${ARCH}/file.txt$\"")
+
+  unset(CMAKE_SIZEOF_VOID_P)
+
+
+  # Check influence of CMP0134 policy with OLD value
+  cmake_policy(SET CMP0134 OLD)
+  # CMAKE_SIZEOF_VOID_P is not set, so search first 32bit registry
+  unset(result)
+  find_file(result NAMES file.txt PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"/32bit/file.txt$\"")
+
+  cmake_policy(SET CMP0134 NEW)
+  # CMAKE_SIZEOF_VOID_P is not set, so search first the HOST architecture registry
+  unset(result)
+  find_file(result NAMES file.txt PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"/${ARCH}/file.txt$\"")
+
+endif()

+ 28 - 0
Tests/RunCMake/find_file/RunCMakeTest.cmake

@@ -5,5 +5,33 @@ run_cmake(FromPrefixPath)
 run_cmake(PrefixInPATH)
 run_cmake(Required)
 run_cmake(NO_CACHE)
+run_cmake(REGISTRY_VIEW-no-view)
+run_cmake(REGISTRY_VIEW-wrong-view)
 
 run_cmake_with_options(FromPATHEnvDebugVar --debug-find-var=PrefixInPATH_File)
+
+if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows")
+  # Tests using the Windows registry
+  find_program(REG NAMES "reg.exe" NO_CACHE)
+  if (REG)
+    ## check host architecture
+    cmake_host_system_information(RESULT result QUERY WINDOWS_REGISTRY "HKCU" SUBKEYS VIEW 64 ERROR_VARIABLE status)
+    if (status STREQUAL "")
+      set(ARCH "64bit")
+    else()
+      set(ARCH "32bit")
+    endif()
+
+    # crete some entries in the registry
+    cmake_path(CONVERT "${RunCMake_SOURCE_DIR}/registry_host${ARCH}.reg" TO_NATIVE_PATH_LIST registry_data)
+    execute_process(COMMAND "${REG}" import "${registry_data}" OUTPUT_QUIET ERROR_QUIET)
+
+    run_cmake_with_options(Registry-query -DARCH=${ARCH})
+
+    # clean-up registry
+    execute_process(COMMAND "${REG}" delete "HKCU\\SOFTWARE\\Classes\\CLSID\\CMake-Tests\\find_file" /f OUTPUT_QUIET ERROR_QUIET)
+    if (ARCH STREQUAL "64bit")
+      execute_process(COMMAND "${REG}" delete "HKCU\\SOFTWARE\\Classes\\WOW6432Node\\CLSID\\CMake-Tests\\find_file" /f OUTPUT_QUIET ERROR_QUIET)
+    endif()
+  endif()
+endif()

+ 0 - 0
Tests/RunCMake/find_file/default.32bit/file.txt


+ 0 - 0
Tests/RunCMake/find_file/default.32bit/file32bit.txt


+ 0 - 0
Tests/RunCMake/find_file/default.64bit/file.txt


+ 0 - 0
Tests/RunCMake/find_file/default.64bit/file64bit.txt


二进制
Tests/RunCMake/find_file/registry_host32bit.reg


二进制
Tests/RunCMake/find_file/registry_host64bit.reg


+ 0 - 0
Tests/RunCMake/find_library/32bit/file.lib


+ 0 - 0
Tests/RunCMake/find_library/32bit/file32bit.lib


+ 0 - 0
Tests/RunCMake/find_library/64bit/file.lib


+ 0 - 0
Tests/RunCMake/find_library/64bit/file64bit.lib


+ 1 - 0
Tests/RunCMake/find_library/REGISTRY_VIEW-no-view-result.txt

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

+ 4 - 0
Tests/RunCMake/find_library/REGISTRY_VIEW-no-view-stderr.txt

@@ -0,0 +1,4 @@
+CMake Error at REGISTRY_VIEW-no-view.cmake:[0-9]+ \(find_library\):
+  find_library missing required argument for "REGISTRY_VIEW"
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)

+ 2 - 0
Tests/RunCMake/find_library/REGISTRY_VIEW-no-view.cmake

@@ -0,0 +1,2 @@
+
+find_library(result NAMES input.txt REGISTRY_VIEW)

+ 1 - 0
Tests/RunCMake/find_library/REGISTRY_VIEW-wrong-view-result.txt

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

+ 4 - 0
Tests/RunCMake/find_library/REGISTRY_VIEW-wrong-view-stderr.txt

@@ -0,0 +1,4 @@
+CMake Error at REGISTRY_VIEW-wrong-view.cmake:[0-9]+ \(find_library\):
+  find_library given invalid value for "REGISTRY_VIEW": WRONG_VIEW
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)

+ 2 - 0
Tests/RunCMake/find_library/REGISTRY_VIEW-wrong-view.cmake

@@ -0,0 +1,2 @@
+
+find_library(result NAMES input.txt REGISTRY_VIEW WRONG_VIEW)

+ 218 - 0
Tests/RunCMake/find_library/Registry-query.cmake

@@ -0,0 +1,218 @@
+
+# helper function for test validation
+function(CHECK query result expression)
+  cmake_language(EVAL CODE
+    "if (NOT (${expression}))
+       message(SEND_ERROR \"wrong value for query '${query}': '${result}'\")
+     endif()")
+endfunction()
+
+cmake_policy(SET CMP0134 NEW)
+
+# HKCU/Software/Classes/CLSID/CMake-Tests/find_library: Query default value
+set(FILE_DIR "[HKCU/Software/Classes/CLSID/CMake-Tests/find_library]")
+set(FILE_DIR2 "[HKCU/Software/Classes/CLSID/CMake-Tests/find_library;(default)]")
+
+unset(result)
+find_library(result NAMES file.lib PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REQUIRED NO_CACHE NO_DEFAULT_PATH)
+check("${FILE_DIR}" "${result}" "result MATCHES \"default.${ARCH}/file.lib$\"")
+
+# query value using special name should be identical to default value
+unset(result)
+find_library(result NAMES file.lib PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR2}" REQUIRED NO_CACHE NO_DEFAULT_PATH)
+check("${FILE_DIR2}" "${result}" "result MATCHES \"default.${ARCH}/file.lib$\"")
+
+unset(result)
+find_library(result NAMES file.lib PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW HOST REQUIRED NO_CACHE NO_DEFAULT_PATH)
+check("${FILE_DIR}" "${result}" "result MATCHES \"default.${ARCH}/file.lib$\"")
+# VIEW TARGET should have same value as VIEW HOST
+unset(result2)
+find_library(result2 NAMES file.lib PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW TARGET REQUIRED NO_CACHE NO_DEFAULT_PATH)
+check("${FILE_DIR}" "${result2}" "result2 STREQUAL result")
+
+if (ARCH STREQUAL "64bit")
+
+  unset(result)
+  find_library(result NAMES file.lib PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 64 REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"default.64bit/file.lib$\"")
+
+  unset(result)
+  find_library(result NAMES file.lib PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 32 REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"default.32bit/file.lib$\"")
+
+  unset(result)
+  find_library(result NAMES file.lib PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 64_32 REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"default.64bit/file.lib$\"")
+
+  unset(result)
+  find_library(result NAMES file.lib PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 32_64 REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"default.32bit/file.lib$\"")
+
+  # check the second view is taken into account
+  unset(result)
+  find_library(result NAMES file32bit.lib PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 64_32 REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"default.32bit/file32bit.lib$\"")
+
+  unset(result)
+  find_library(result NAMES file64bit.lib PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 32_64 REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"default.64bit/file64bit.lib$\"")
+  unset(result)
+
+  # check the both views are taken into account
+  unset(result)
+  find_library(result NAMES file32bit.lib PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW BOTH REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"default.32bit/file32bit.lib$\"")
+
+  unset(result)
+  find_library(result NAMES file64bit.lib PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW BOTH REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"default.64bit/file64bit.lib$\"")
+  unset(result)
+
+else() # 32bit
+
+  # no 64bit registry: file not found
+  unset(result)
+  find_library(result NAMES file.lib PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 64 NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"result-NOTFOUND$\"")
+
+  unset(result)
+  find_library(result NAMES file.lib PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 32 REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"default.32bit/file.lib$\"")
+
+  # views 64_32 and 32_64 give same result
+  unset(result)
+  find_library(result NAMES file.lib PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 64_32 REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"default.32bit/file.lib$\"")
+
+  unset(result)
+  find_library(result NAMES file.lib PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 32_64 REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"default.32bit/file.lib$\"")
+
+  # check the both views are usable on 32bit platforms
+  unset(result)
+  find_library(result NAMES file.lib PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW BOTH REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"default.32bit/file.lib$\"")
+
+endif()
+
+# HKCU/Software/Classes/CLSID/CMake-Tests/find_library: Query specific value
+set(FILE_DIR "[{|}HKCU/Software/Classes/CLSID/CMake-Tests/find_library|FILE_DIR]")
+set(FILE_DIR2 "[HKCU\\Software\\Classes\\CLSID\\CMake-Tests\\find_library;FILE_DIR]")
+
+unset(result)
+find_library(result NAMES file.lib HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REQUIRED NO_CACHE NO_DEFAULT_PATH)
+check("${FILE_DIR}" "${result}" "result MATCHES \"/${ARCH}/file.lib$\"")
+
+# query value using special name should be identical to default value
+unset(result)
+find_library(result NAMES file.lib HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR2}" REQUIRED NO_CACHE NO_DEFAULT_PATH)
+check("${FILE_DIR2}" "${result}" "result MATCHES \"/${ARCH}/file.lib$\"")
+
+unset(result)
+find_library(result NAMES file.lib HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW HOST REQUIRED NO_CACHE NO_DEFAULT_PATH)
+check("${FILE_DIR}" "${result}" "result MATCHES \"/${ARCH}/file.lib$\"")
+# VIEW TARGET should have same value as VIEW HOST
+unset(result2)
+find_library(result2 NAMES file.lib HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW TARGET REQUIRED NO_CACHE NO_DEFAULT_PATH)
+check("${FILE_DIR}" "${result2}" "result2 STREQUAL result")
+
+if (ARCH STREQUAL "64bit")
+
+  unset(result)
+  find_library(result NAMES file.lib HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 64 REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"/64bit/file.lib$\"")
+
+  unset(result)
+  find_library(result NAMES file.lib HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 32 REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"/32bit/file.lib$\"")
+
+  unset(result)
+  find_library(result NAMES file.lib HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 64_32 REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"/64bit/file.lib$\"")
+
+  unset(result)
+  find_library(result NAMES file.lib HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 32_64 REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"/32bit/file.lib$\"")
+
+  # check the second view is taken into account
+  unset(result)
+  find_library(result NAMES file32bit.lib HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 64_32 REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"/32bit/file32bit.lib$\"")
+
+  unset(result)
+  find_library(result NAMES file64bit.lib HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 32_64 REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"/64bit/file64bit.lib$\"")
+
+  # check the both views are taken into account
+  unset(result)
+  find_library(result NAMES file32bit.lib HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW BOTH NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"/32bit/file32bit.lib$\"")
+
+  unset(result)
+  find_library(result NAMES file64bit.lib HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW BOTH REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"/64bit/file64bit.lib$\"")
+
+else() # 32bit
+
+  # no 64bit registry: file not found
+  unset(result)
+  find_library(result NAMES file.lib HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 64 NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"result-NOTFOUND$\"")
+
+  unset(result)
+  find_library(result NAMES file.lib HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 32 REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"/32bit/file.lib$\"")
+
+  unset(result)
+  find_library(result NAMES file.lib HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 64_32 REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"/32bit/file.lib$\"")
+
+  unset(result)
+  find_library(result NAMES file.lib HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 32_64 REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"/32bit/file.lib$\"")
+
+  unset(result)
+  find_library(result NAMES file.lib HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW BOTH REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"/32bit/file.lib$\"")
+
+endif()
+
+if (ARCH STREQUAL "64bit")
+
+  # Check influence of variable CMAKE_SIZEOF_VOID_P
+  set(CMAKE_SIZEOF_VOID_P 8)
+  unset(result)
+  find_library(result NAMES file.lib PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW TARGET REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"/64bit/file.lib$\"")
+
+  unset(result)
+  find_library(result NAMES file.lib PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW HOST REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"/${ARCH}/file.lib$\"")
+
+
+  set(CMAKE_SIZEOF_VOID_P 4)
+  unset(result)
+  find_library(result NAMES file.lib PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW TARGET REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"/32bit/file.lib$\"")
+
+  unset(result)
+  find_library(result NAMES file.lib PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW HOST REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"/${ARCH}/file.lib$\"")
+
+  unset(CMAKE_SIZEOF_VOID_P)
+
+
+  # Check influence of CMP0134 policy with OLD value
+  cmake_policy(SET CMP0134 OLD)
+  # CMAKE_SIZEOF_VOID_P is not set, so search first 32bit registry
+  unset(result)
+  find_library(result NAMES file.lib PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"/32bit/file.lib$\"")
+
+  cmake_policy(SET CMP0134 NEW)
+  # CMAKE_SIZEOF_VOID_P is not set, so search first the HOST architecture registry
+  unset(result)
+  find_library(result NAMES file.lib PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"/${ARCH}/file.lib$\"")
+
+endif()

+ 28 - 0
Tests/RunCMake/find_library/RunCMakeTest.cmake

@@ -11,7 +11,35 @@ endif()
 run_cmake(PrefixInPATH)
 run_cmake(Required)
 run_cmake(NO_CACHE)
+run_cmake(REGISTRY_VIEW-no-view)
+run_cmake(REGISTRY_VIEW-wrong-view)
 
 run_cmake_script(FromScriptMode "-DTEMP_DIR=${RunCMake_BINARY_DIR}/FromScriptMode-temp")
 
 run_cmake_with_options(FromPATHEnvDebugVar --debug-find-var=CREATED_LIBRARY)
+
+if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows")
+  # Tests using the Windows registry
+  find_program(REG NAMES "reg.exe" NO_CACHE)
+  if (REG)
+    ## check host architecture
+    cmake_host_system_information(RESULT result QUERY WINDOWS_REGISTRY "HKCU" SUBKEYS VIEW 64 ERROR_VARIABLE status)
+    if (status STREQUAL "")
+      set(ARCH "64bit")
+    else()
+      set(ARCH "32bit")
+    endif()
+
+    # crete some entries in the registry
+    cmake_path(CONVERT "${RunCMake_SOURCE_DIR}/registry_host${ARCH}.reg" TO_NATIVE_PATH_LIST registry_data)
+    execute_process(COMMAND "${REG}" import "${registry_data}" OUTPUT_QUIET ERROR_QUIET)
+
+    run_cmake_with_options(Registry-query -DARCH=${ARCH})
+
+    # clean-up registry
+    execute_process(COMMAND "${REG}" delete "HKCU\\SOFTWARE\\Classes\\CLSID\\CMake-Tests\\find_library" /f OUTPUT_QUIET ERROR_QUIET)
+    if (ARCH STREQUAL "64bit")
+      execute_process(COMMAND "${REG}" delete "HKCU\\SOFTWARE\\Classes\\WOW6432Node\\CLSID\\CMake-Tests\\find_library" /f OUTPUT_QUIET ERROR_QUIET)
+    endif()
+  endif()
+endif()

+ 0 - 0
Tests/RunCMake/find_library/default.32bit/file.lib


+ 0 - 0
Tests/RunCMake/find_library/default.32bit/file32bit.lib


+ 0 - 0
Tests/RunCMake/find_library/default.64bit/file.lib


+ 0 - 0
Tests/RunCMake/find_library/default.64bit/file64bit.lib


二进制
Tests/RunCMake/find_library/registry_host32bit.reg


二进制
Tests/RunCMake/find_library/registry_host64bit.reg


+ 4 - 0
Tests/RunCMake/find_package/32bit/RegistryView32Config.cmake

@@ -0,0 +1,4 @@
+
+if (NOT EXPECTED_LOCATION STREQUAL "32bit")
+  message (SEND_ERROR "RegistryViewConfig: location is '32bit' but expects '${EXPECTED_LOCATION}'")
+endif()

+ 4 - 0
Tests/RunCMake/find_package/32bit/RegistryViewConfig.cmake

@@ -0,0 +1,4 @@
+
+if (NOT EXPECTED_LOCATION STREQUAL "32bit")
+  message (SEND_ERROR "RegistryViewConfig: location is '32bit' but expects '${EXPECTED_LOCATION}'")
+endif()

+ 4 - 0
Tests/RunCMake/find_package/64bit/RegistryView64Config.cmake

@@ -0,0 +1,4 @@
+
+if (NOT EXPECTED_LOCATION STREQUAL "64bit")
+  message (SEND_ERROR "RegistryViewConfig: location is '64bit' but expects '${EXPECTED_LOCATION}'")
+endif()

+ 4 - 0
Tests/RunCMake/find_package/64bit/RegistryViewConfig.cmake

@@ -0,0 +1,4 @@
+
+if (NOT EXPECTED_LOCATION STREQUAL "64bit")
+  message (SEND_ERROR "RegistryViewConfig: location is '64bit' but expects '${EXPECTED_LOCATION}'")
+endif()

+ 11 - 0
Tests/RunCMake/find_package/FindRegistryView.cmake

@@ -0,0 +1,11 @@
+
+if (EXPECTED_REGISTRY_VIEW STREQUAL "UNDEFINED")
+  if (DEFINED ${CMAKE_FIND_PACKAGE_NAME}_FIND_REGISTRY_VIEW)
+    message(SEND_ERROR "${CMAKE_FIND_PACKAGE_NAME}_FIND_REGISTRY_VIEW: unexpectedly defined as '${${CMAKE_FIND_PACKAGE_NAME}_FIND_REGISTRY_VIEW}' instead of '${EXPECTED_REGISTRY_VIEW}'")
+  endif()
+  return()
+endif()
+
+if (NOT ${CMAKE_FIND_PACKAGE_NAME}_FIND_REGISTRY_VIEW STREQUAL EXPECTED_REGISTRY_VIEW)
+  message(SEND_ERROR "${CMAKE_FIND_PACKAGE_NAME}_FIND_REGISTRY_VIEW: '${${CMAKE_FIND_PACKAGE_NAME}_FIND_REGISTRY_VIEW}' instead of '${EXPECTED_REGISTRY_VIEW}'")
+endif()

+ 1 - 0
Tests/RunCMake/find_package/REGISTRY_VIEW-no-view-result.txt

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

+ 4 - 0
Tests/RunCMake/find_package/REGISTRY_VIEW-no-view-stderr.txt

@@ -0,0 +1,4 @@
+CMake Error at REGISTRY_VIEW-no-view.cmake:[0-9]+ \(find_package\):
+  find_package missing required argument for "REGISTRY_VIEW"
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)

+ 2 - 0
Tests/RunCMake/find_package/REGISTRY_VIEW-no-view.cmake

@@ -0,0 +1,2 @@
+
+find_package(result NAMES input.txt REGISTRY_VIEW)

+ 16 - 0
Tests/RunCMake/find_package/REGISTRY_VIEW-propagated.cmake

@@ -0,0 +1,16 @@
+
+set (CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}")
+
+# when REGISTRY_VIEW is not specified, should not be defined in module
+set (EXPECTED_REGISTRY_VIEW "UNDEFINED")
+find_package(RegistryView)
+
+# query package to check if variable is propagated correctly
+set(EXPECTED_REGISTRY_VIEW "TARGET")
+find_package(RegistryView REGISTRY_VIEW TARGET)
+
+set(EXPECTED_REGISTRY_VIEW "64_32")
+find_package(RegistryView REGISTRY_VIEW 64_32)
+
+set(EXPECTED_REGISTRY_VIEW "32")
+find_package(RegistryView REGISTRY_VIEW 32)

+ 1 - 0
Tests/RunCMake/find_package/REGISTRY_VIEW-wrong-view-result.txt

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

+ 4 - 0
Tests/RunCMake/find_package/REGISTRY_VIEW-wrong-view-stderr.txt

@@ -0,0 +1,4 @@
+CMake Error at REGISTRY_VIEW-wrong-view.cmake:[0-9]+ \(find_package\):
+  find_package given invalid value for "REGISTRY_VIEW": WRONG_VIEW
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)

+ 2 - 0
Tests/RunCMake/find_package/REGISTRY_VIEW-wrong-view.cmake

@@ -0,0 +1,2 @@
+
+find_package(result NAMES input.txt REGISTRY_VIEW WRONG_VIEW)

+ 216 - 0
Tests/RunCMake/find_package/Registry-query.cmake

@@ -0,0 +1,216 @@
+
+# helper macro for test clean-up
+macro(CLEAN)
+  unset(RegistryView_DIR CACHE)
+  unset(RegistryView_FOUND)
+  unset(RegistryView64_DIR CACHE)
+  unset(RegistryView64_FOUND)
+  unset(RegistryView32_DIR CACHE)
+  unset(RegistryView32_FOUND)
+endmacro()
+
+
+cmake_policy(SET CMP0134 NEW)
+
+# HKCU/Software/Classes/CLSID/CMake-Tests/find_package: Query default value
+set(FILE_DIR "[HKCU/Software/Classes/CLSID/CMake-Tests/find_package]")
+set(FILE_DIR2 "[HKCU/Software/Classes/CLSID/CMake-Tests/find_package;(default)]")
+
+set(EXPECTED_LOCATION "default.${ARCH}")
+
+find_package(RegistryView PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REQUIRED NO_DEFAULT_PATH)
+clean()
+
+# query value using special name should be identical to default value
+find_package(RegistryView PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR2}" REQUIRED NO_DEFAULT_PATH)
+clean()
+
+find_package(RegistryView PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW HOST REQUIRED NO_DEFAULT_PATH)
+clean()
+
+# VIEW TARGET should have same value as VIEW HOST
+find_package(RegistryView PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW TARGET REQUIRED NO_DEFAULT_PATH)
+clean()
+
+if (ARCH STREQUAL "64bit")
+
+  set(EXPECTED_LOCATION "default.64bit")
+  find_package(RegistryView PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 64 REQUIRED NO_DEFAULT_PATH)
+  clean()
+
+  set(EXPECTED_LOCATION "default.32bit")
+  find_package(RegistryView PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 32 REQUIRED NO_DEFAULT_PATH)
+  clean()
+
+  set(EXPECTED_LOCATION "default.64bit")
+  find_package(RegistryView PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 64_32 REQUIRED NO_DEFAULT_PATH)
+  clean()
+
+  set(EXPECTED_LOCATION "default.32bit")
+  find_package(RegistryView PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 32_64 REQUIRED NO_DEFAULT_PATH)
+  clean()
+
+  # check the second view is taken into account
+  set(EXPECTED_LOCATION "default.32bit")
+  find_package(RegistryView32 PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 64_32 REQUIRED NO_DEFAULT_PATH)
+  clean()
+
+  set(EXPECTED_LOCATION "default.64bit")
+  find_package(RegistryView64 PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 32_64 REQUIRED NO_DEFAULT_PATH)
+  clean()
+
+  # check the both views are taken into account
+  set(EXPECTED_LOCATION "default.32bit")
+  find_package(RegistryView32 PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW BOTH REQUIRED NO_DEFAULT_PATH)
+  clean()
+
+  set(EXPECTED_LOCATION "default.64bit")
+  find_package(RegistryView64 PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW BOTH REQUIRED NO_DEFAULT_PATH)
+  clean()
+
+else() # 32bit
+
+  # no 64bit registry: file not found
+  find_package(RegistryView PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 64 NO_DEFAULT_PATH)
+  if (RegistryView_FOUND)
+    message (SEND_ERROR "Unexpectedly found file '${RegistryView_DIR}/RegistryViewConfog.cmake'")
+  endif()
+  clean()
+
+  set(EXPECTED_LOCATION "default.32bit")
+
+  find_package(RegistryView PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 32 REQUIRED NO_DEFAULT_PATH)
+  clean()
+
+  # views 64_32 and 32_64 give same result
+  find_package(RegistryView PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 64_32 REQUIRED NO_DEFAULT_PATH)
+  clean()
+
+  find_package(RegistryView PATHS "${CMAKE_ CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 32_64 REQUIRED NO_DEFAULT_PATH)
+  clean()
+
+  # check the both views are usable on 32bit platforms
+  find_package(RegistryView PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW BOTH REQUIRED NO_DEFAULT_PATH)
+  clean()
+
+endif()
+
+
+# HKCU/Software/Classes/CLSID/CMake-Tests/find_package: Query specific value
+set(FILE_DIR "[{|}HKCU/Software/Classes/CLSID/CMake-Tests/find_package|FILE_DIR]")
+set(FILE_DIR2 "[HKCU\\Software\\Classes\\CLSID\\CMake-Tests\\find_package;FILE_DIR]")
+
+set(EXPECTED_LOCATION "${ARCH}")
+
+find_package(RegistryView HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REQUIRED NO_DEFAULT_PATH)
+clean()
+
+# query value using special name should be identical to default value
+find_package(RegistryView HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR2}" REQUIRED NO_DEFAULT_PATH)
+clean()
+
+find_package(RegistryView HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW HOST REQUIRED NO_DEFAULT_PATH)
+clean()
+# VIEW TARGET should have same value as VIEW HOST
+find_package(RegistryView HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW TARGET REQUIRED NO_DEFAULT_PATH)
+clean()
+
+if (ARCH STREQUAL "64bit")
+
+  set(EXPECTED_LOCATION "64bit")
+  find_package(RegistryView HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 64 REQUIRED NO_DEFAULT_PATH)
+  clean()
+
+  set(EXPECTED_LOCATION "32bit")
+  find_package(RegistryView HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 32 REQUIRED NO_DEFAULT_PATH)
+  clean()
+
+  set(EXPECTED_LOCATION "64bit")
+  find_package(RegistryView HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 64_32 REQUIRED NO_DEFAULT_PATH)
+  clean()
+
+  set(EXPECTED_LOCATION "32bit")
+  find_package(RegistryView HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 32_64 REQUIRED NO_DEFAULT_PATH)
+  clean()
+
+  # check the second view is taken into account
+  find_package(RegistryView32 HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 64_32 REQUIRED NO_DEFAULT_PATH)
+  clean()
+
+  set(EXPECTED_LOCATION "64bit")
+  find_package(RegistryView64 HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 32_64 REQUIRED NO_DEFAULT_PATH)
+  clean()
+
+  # check the both views are taken into account
+  set(EXPECTED_LOCATION "32bit")
+  find_package(RegistryView32 HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW BOTH REQUIRED NO_DEFAULT_PATH)
+  clean()
+
+  set(EXPECTED_LOCATION "64bit")
+  find_package(RegistryView64 NAMES HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW BOTH REQUIRED NO_DEFAULT_PATH)
+  clean()
+
+else() # 32bit
+
+  # no 64bit registry: file not found
+  find_package(RegistryView HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 64 NO_DEFAULT_PATH)
+  if (RegistryView_FOUND)
+    message (SEND_ERROR "Unexpectedly found file '${RegistryView_DIR}/RegistryViewConfog.cmake'")
+  endif()
+  clean()
+
+  set(EXPECTED_LOCATION "32bit")
+
+  find_package(RegistryView HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 32 REQUIRED NO_DEFAULT_PATH)
+  clean()
+
+  find_package(RegistryView HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 64_32 REQUIRED NO_DEFAULT_PATH)
+  clean()
+
+  find_package(RegistryView HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 32_64 REQUIRED NO_DEFAULT_PATH)
+  clean()
+
+  # check the both views are taken into account
+  find_package(RegistryView HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW BOTH REQUIRED NO_DEFAULT_PATH)
+  clean()
+
+endif()
+
+if (ARCH STREQUAL "64bit")
+
+  # Check influence of variable CMAKE_SIZEOF_VOID_P
+  set(CMAKE_SIZEOF_VOID_P 8)
+  set(EXPECTED_LOCATION "64bit")
+  find_package(RegistryView PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW TARGET)
+  clean()
+
+  find_package(RegistryView PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW HOST)
+  clean()
+
+
+  set(CMAKE_SIZEOF_VOID_P 4)
+  set(EXPECTED_LOCATION "32bit")
+  find_package(RegistryView PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW TARGET)
+  clean()
+
+  set(EXPECTED_LOCATION "64bit")
+  find_package(RegistryView PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW HOST)
+  clean()
+
+  unset(CMAKE_SIZEOF_VOID_P)
+
+
+  # Check influence of CMP0134 policy with OLD value
+  cmake_policy(SET CMP0134 OLD)
+  # CMAKE_SIZEOF_VOID_P is not set, so search first 32bit registry
+  set(EXPECTED_LOCATION "32bit")
+  find_package(RegistryView PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}")
+  clean()
+
+  cmake_policy(SET CMP0134 NEW)
+  # CMAKE_SIZEOF_VOID_P is not set, so search first the HOST architecture registry
+  set(EXPECTED_LOCATION "64bit")
+  find_package(RegistryView PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}")
+  clean()
+
+endif()

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

@@ -51,8 +51,38 @@ run_cmake(VersionRangeConfigStd2)
 run_cmake_with_options(IgnoreInstallPrefix  "-DCMAKE_INSTALL_PREFIX=${RunCMake_SOURCE_DIR}/PackageRoot/foo/cmake_root")
 run_cmake(IgnorePath)
 run_cmake(IgnorePrefixPath)
+run_cmake(REGISTRY_VIEW-no-view)
+run_cmake(REGISTRY_VIEW-wrong-view)
+run_cmake(REGISTRY_VIEW-propagated)
+
 if(UNIX
     AND NOT MSYS # FIXME: This works on CYGWIN but not on MSYS
     )
   run_cmake(SetFoundResolved)
 endif()
+
+if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows")
+  # Tests using the Windows registry
+  find_program(REG NAMES "reg.exe" NO_CACHE)
+  if (REG)
+    ## check host architecture
+    cmake_host_system_information(RESULT result QUERY WINDOWS_REGISTRY "HKCU" SUBKEYS VIEW 64 ERROR_VARIABLE status)
+    if (status STREQUAL "")
+      set(ARCH "64bit")
+    else()
+      set(ARCH "32bit")
+    endif()
+
+    # crete some entries in the registry
+    cmake_path(CONVERT "${RunCMake_SOURCE_DIR}/registry_host${ARCH}.reg" TO_NATIVE_PATH_LIST registry_data)
+    execute_process(COMMAND "${REG}" import "${registry_data}" OUTPUT_QUIET ERROR_QUIET)
+
+    run_cmake_with_options(Registry-query -DARCH=${ARCH})
+
+    # clean-up registry
+    execute_process(COMMAND "${REG}" delete "HKCU\\SOFTWARE\\Classes\\CLSID\\CMake-Tests\\find_package" /f OUTPUT_QUIET ERROR_QUIET)
+    if (ARCH STREQUAL "64bit")
+      execute_process(COMMAND "${REG}" delete "HKCU\\SOFTWARE\\Classes\\WOW6432Node\\CLSID\\CMake-Tests\\find_package" /f OUTPUT_QUIET ERROR_QUIET)
+    endif()
+  endif()
+endif()

+ 4 - 0
Tests/RunCMake/find_package/default.32bit/RegistryView32Config.cmake

@@ -0,0 +1,4 @@
+
+if (NOT EXPECTED_LOCATION STREQUAL "default.32bit")
+  message (SEND_ERROR "RegistryViewConfig: location is 'default.32bit' but expects '${EXPECTED_LOCATION}'")
+endif()

+ 4 - 0
Tests/RunCMake/find_package/default.32bit/RegistryViewConfig.cmake

@@ -0,0 +1,4 @@
+
+if (NOT EXPECTED_LOCATION STREQUAL "default.32bit")
+  message (SEND_ERROR "RegistryViewConfig: location is 'default.32bit' but expects '${EXPECTED_LOCATION}'")
+endif()

+ 4 - 0
Tests/RunCMake/find_package/default.64bit/RegistryView64Config.cmake

@@ -0,0 +1,4 @@
+
+if (NOT EXPECTED_LOCATION STREQUAL "default.64bit")
+  message (SEND_ERROR "RegistryViewConfig: location is 'default.64bit' but expects '${EXPECTED_LOCATION}'")
+endif()

+ 4 - 0
Tests/RunCMake/find_package/default.64bit/RegistryViewConfig.cmake

@@ -0,0 +1,4 @@
+
+if (NOT EXPECTED_LOCATION STREQUAL "default.64bit")
+  message (SEND_ERROR "RegistryViewConfig: location is 'default.64bit' but expects '${EXPECTED_LOCATION}'")
+endif()

二进制
Tests/RunCMake/find_package/registry_host32bit.reg


二进制
Tests/RunCMake/find_package/registry_host64bit.reg


+ 0 - 0
Tests/RunCMake/find_path/32bit/file.txt


+ 0 - 0
Tests/RunCMake/find_path/32bit/file32bit.txt


+ 0 - 0
Tests/RunCMake/find_path/64bit/file.txt


+ 0 - 0
Tests/RunCMake/find_path/64bit/file64bit.txt


+ 1 - 0
Tests/RunCMake/find_path/REGISTRY_VIEW-no-view-result.txt

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

+ 4 - 0
Tests/RunCMake/find_path/REGISTRY_VIEW-no-view-stderr.txt

@@ -0,0 +1,4 @@
+CMake Error at REGISTRY_VIEW-no-view.cmake:[0-9]+ \(find_path\):
+  find_path missing required argument for "REGISTRY_VIEW"
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)

+ 2 - 0
Tests/RunCMake/find_path/REGISTRY_VIEW-no-view.cmake

@@ -0,0 +1,2 @@
+
+find_path(result NAMES input.txt REGISTRY_VIEW)

+ 1 - 0
Tests/RunCMake/find_path/REGISTRY_VIEW-wrong-view-result.txt

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

+ 4 - 0
Tests/RunCMake/find_path/REGISTRY_VIEW-wrong-view-stderr.txt

@@ -0,0 +1,4 @@
+CMake Error at REGISTRY_VIEW-wrong-view.cmake:[0-9]+ \(find_path\):
+  find_path given invalid value for "REGISTRY_VIEW": WRONG_VIEW
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)

+ 2 - 0
Tests/RunCMake/find_path/REGISTRY_VIEW-wrong-view.cmake

@@ -0,0 +1,2 @@
+
+find_path(result NAMES input.txt REGISTRY_VIEW WRONG_VIEW)

+ 218 - 0
Tests/RunCMake/find_path/Registry-query.cmake

@@ -0,0 +1,218 @@
+
+# helper function for test validation
+function(CHECK query result expression)
+  cmake_language(EVAL CODE
+    "if (NOT (${expression}))
+       message(SEND_ERROR \"wrong value for query '${query}': '${result}'\")
+     endif()")
+endfunction()
+
+cmake_policy(SET CMP0134 NEW)
+
+# HKCU/Software/Classes/CLSID/CMake-Tests/find_path: Query default value
+set(FILE_DIR "[HKCU/Software/Classes/CLSID/CMake-Tests/find_path]")
+set(FILE_DIR2 "[HKCU/Software/Classes/CLSID/CMake-Tests/find_path;(default)]")
+
+unset(result)
+find_path(result NAMES file.txt PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REQUIRED NO_CACHE NO_DEFAULT_PATH)
+check("${FILE_DIR}" "${result}" "result MATCHES \"default.${ARCH}/$\"")
+
+# query value using special name should be identical to default value
+unset(result)
+find_path(result NAMES file.txt PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR2}" REQUIRED NO_CACHE NO_DEFAULT_PATH)
+check("${FILE_DIR2}" "${result}" "result MATCHES \"default.${ARCH}/$\"")
+
+unset(result)
+find_path(result NAMES file.txt PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW HOST REQUIRED NO_CACHE NO_DEFAULT_PATH)
+check("${FILE_DIR}" "${result}" "result MATCHES \"default.${ARCH}/$\"")
+# VIEW TARGET should have same value as VIEW HOST
+unset(result2)
+find_path(result2 NAMES file.txt PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW TARGET REQUIRED NO_CACHE NO_DEFAULT_PATH)
+check("${FILE_DIR}" "${result2}" "result2 STREQUAL result")
+
+if (ARCH STREQUAL "64bit")
+
+  unset(result)
+  find_path(result NAMES file.txt PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 64 REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"default.64bit/$\"")
+
+  unset(result)
+  find_path(result NAMES file.txt PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 32 REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"default.32bit/$\"")
+
+  unset(result)
+  find_path(result NAMES file.txt PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 64_32 REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"default.64bit/$\"")
+  unset(result)
+
+  find_path(result NAMES file.txt PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 32_64 REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"default.32bit/$\"")
+
+  # check the second view is taken into account
+  unset(result)
+  find_path(result NAMES file32bit.txt PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 64_32 REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"default.32bit/$\"")
+
+  unset(result)
+  find_path(result NAMES file64bit.txt PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 32_64 REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"default.64bit/$\"")
+
+  # check the both views are taken into account
+  unset(result)
+  find_path(result NAMES file32bit.txt PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW BOTH REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"default.32bit/$\"")
+
+  unset(result)
+  find_path(result NAMES file64bit.txt PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW BOTH REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"default.64bit/$\"")
+
+else() # 32bit
+
+  # no 64bit registry: file not found
+  unset(result)
+  find_path(result NAMES file.txt PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 64 NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"result-NOTFOUND$\"")
+
+  unset(result)
+  find_path(result NAMES file.txt PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 32 REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"default.32bit/$\"")
+
+  # views 64_32 and 32_64 give same result
+  unset(result)
+  find_path(result NAMES file.txt PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 64_32 REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"default.32bit/$\"")
+
+  unset(result)
+  find_path(result NAMES file.txt PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 32_64 REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"default.32bit/$\"")
+
+  # check the both views are usable on 32bit platforms
+  unset(result)
+  find_path(result NAMES file.txt PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW BOTH REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"default.32bit/$\"")
+
+endif()
+
+
+# HKCU/Software/Classes/CLSID/CMake-Tests/find_path: Query specific value
+set(FILE_DIR "[{|}HKCU/Software/Classes/CLSID/CMake-Tests/find_path|FILE_DIR]")
+set(FILE_DIR2 "[HKCU\\Software\\Classes\\CLSID\\CMake-Tests\\find_path;FILE_DIR]")
+
+unset(result)
+find_path(result NAMES file.txt HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" NO_CACHE REQUIRED NO_DEFAULT_PATH)
+check("${FILE_DIR}" "${result}" "result MATCHES \"/${ARCH}/$\"")
+
+# query value using special name should be identical to default value
+unset(result)
+find_path(result NAMES file.txt HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR2}" NO_CACHE REQUIRED NO_DEFAULT_PATH)
+check("${FILE_DIR2}" "${result}" "result MATCHES \"/${ARCH}/$\"")
+
+unset(result)
+find_path(result NAMES file.txt HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW HOST REQUIRED NO_CACHE NO_DEFAULT_PATH)
+check("${FILE_DIR}" "${result}" "result MATCHES \"/${ARCH}/$\"")
+# VIEW TARGET should have same value as VIEW HOST
+unset(result2)
+find_path(result2 NAMES file.txt HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW TARGET REQUIRED NO_CACHE NO_DEFAULT_PATH)
+check("${FILE_DIR}" "${result2}" "result2 STREQUAL result")
+
+if (ARCH STREQUAL "64bit")
+
+unset(result)
+find_path(result NAMES file.txt HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 64 REQUIRED NO_CACHE NO_DEFAULT_PATH)
+check("${FILE_DIR}" "${result}" "result MATCHES \"/64bit/$\"")
+
+unset(result)
+find_path(result NAMES file.txt HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 32 REQUIRED NO_CACHE NO_DEFAULT_PATH)
+check("${FILE_DIR}" "${result}" "result MATCHES \"/32bit/$\"")
+
+unset(result)
+find_path(result NAMES file.txt HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 64_32 REQUIRED NO_CACHE NO_DEFAULT_PATH)
+check("${FILE_DIR}" "${result}" "result MATCHES \"/64bit/$\"")
+
+unset(result)
+find_path(result NAMES file.txt HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 32_64 REQUIRED NO_CACHE NO_DEFAULT_PATH)
+check("${FILE_DIR}" "${result}" "result MATCHES \"/32bit/$\"")
+
+# check the second view is taken into account
+unset(result)
+find_path(result NAMES file32bit.txt HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 64_32 REQUIRED NO_CACHE NO_DEFAULT_PATH)
+check("${FILE_DIR}" "${result}" "result MATCHES \"/32bit/$\"")
+
+unset(result)
+find_path(result NAMES file64bit.txt HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 32_64 REQUIRED NO_CACHE NO_DEFAULT_PATH)
+check("${FILE_DIR}" "${result}" "result MATCHES \"/64bit/$\"")
+
+# check the both views are taken into account
+unset(result)
+find_path(result NAMES file32bit.txt HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW BOTH REQUIRED NO_CACHE NO_DEFAULT_PATH)
+check("${FILE_DIR}" "${result}" "result MATCHES \"/32bit/$\"")
+
+unset(result)
+find_path(result NAMES file64bit.txt HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW BOTH REQUIRED NO_CACHE NO_DEFAULT_PATH)
+check("${FILE_DIR}" "${result}" "result MATCHES \"/64bit/$\"")
+
+else() # 32bit
+
+  # no 64bit registry: file not found
+  unset(result)
+  find_path(result NAMES file.txt HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 64 NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"result-NOTFOUND$\"")
+
+  unset(result)
+  find_path(result NAMES file.txt HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 32 REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"/32bit/$\"")
+
+  unset(result)
+  find_path(result NAMES file.txt HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 64_32 REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"/32bit/$\"")
+
+  unset(result)
+  find_path(result NAMES file.txt HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW 32_64 REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"/32bit/$\"")
+
+  unset(result)
+  find_path(result NAMES file.txt HINTS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW BOTH REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"/32bit/$\"")
+
+endif()
+
+
+if (ARCH STREQUAL "64bit")
+
+  # Check influence of variable CMAKE_SIZEOF_VOID_P
+  set(CMAKE_SIZEOF_VOID_P 8)
+  unset(result)
+  find_path(result NAMES file.txt PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW TARGET REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"/64bit/$\"")
+
+  unset(result)
+  find_path(result NAMES file.txt PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW HOST REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"/${ARCH}/$\"")
+
+
+  set(CMAKE_SIZEOF_VOID_P 4)
+  unset(result)
+  find_path(result NAMES file.txt PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW TARGET REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"/32bit/$\"")
+
+  unset(result)
+  find_path(result NAMES file.txt PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" REGISTRY_VIEW HOST REQUIRED NO_CACHE NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"/${ARCH}/$\"")
+
+  unset(CMAKE_SIZEOF_VOID_P)
+
+
+  # Check influence of CMP0134 policy with OLD value
+  cmake_policy(SET CMP0134 OLD)
+  # CMAKE_SIZEOF_VOID_P is not set, so search first 32bit registry
+  unset(result)
+  find_path(result NAMES file.txt PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" NO_CACHE REQUIRED NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"/32bit/$\"")
+
+  cmake_policy(SET CMP0134 NEW)
+  # CMAKE_SIZEOF_VOID_P is not set, so search first the HOST architecture registry
+  unset(result)
+  find_path(result NAMES file.txt PATHS "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_DIR}" NO_CACHE REQUIRED NO_DEFAULT_PATH)
+  check("${FILE_DIR}" "${result}" "result MATCHES \"/${ARCH}/$\"")
+
+endif()

+ 28 - 0
Tests/RunCMake/find_path/RunCMakeTest.cmake

@@ -5,9 +5,37 @@ run_cmake(FromPATHEnv)
 run_cmake(PrefixInPATH)
 run_cmake(Required)
 run_cmake(NO_CACHE)
+run_cmake(REGISTRY_VIEW-no-view)
+run_cmake(REGISTRY_VIEW-wrong-view)
 
 if(APPLE)
   run_cmake(FrameworksWithSubdirs)
 endif()
 
 run_cmake_with_options(FromPATHEnvDebugVar --debug-find-var=PATH_IN_ENV_PATH)
+
+if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows")
+  # Tests using the Windows registry
+  find_program(REG NAMES "reg.exe" NO_CACHE)
+  if (REG)
+    ## check host architecture
+    cmake_host_system_information(RESULT result QUERY WINDOWS_REGISTRY "HKCU" SUBKEYS VIEW 64 ERROR_VARIABLE status)
+    if (status STREQUAL "")
+      set(ARCH "64bit")
+    else()
+      set(ARCH "32bit")
+    endif()
+
+    # crete some entries in the registry
+    cmake_path(CONVERT "${RunCMake_SOURCE_DIR}/registry_host${ARCH}.reg" TO_NATIVE_PATH_LIST registry_data)
+    execute_process(COMMAND "${REG}" import "${registry_data}" OUTPUT_QUIET ERROR_QUIET)
+
+    run_cmake_with_options(Registry-query -DARCH=${ARCH})
+
+    # clean-up registry
+    execute_process(COMMAND "${REG}" delete "HKCU\\SOFTWARE\\Classes\\CLSID\\CMake-Tests\\find_path" /f OUTPUT_QUIET ERROR_QUIET)
+    if (ARCH STREQUAL "64bit")
+      execute_process(COMMAND "${REG}" delete "HKCU\\SOFTWARE\\Classes\\WOW6432Node\\CLSID\\CMake-Tests\\find_path" /f OUTPUT_QUIET ERROR_QUIET)
+    endif()
+  endif()
+endif()

+ 0 - 0
Tests/RunCMake/find_path/default.32bit/file.txt


+ 0 - 0
Tests/RunCMake/find_path/default.32bit/file32bit.txt


+ 0 - 0
Tests/RunCMake/find_path/default.64bit/file.txt


+ 0 - 0
Tests/RunCMake/find_path/default.64bit/file64bit.txt


二进制
Tests/RunCMake/find_path/registry_host32bit.reg


二进制
Tests/RunCMake/find_path/registry_host64bit.reg


+ 0 - 0
Tests/RunCMake/find_program/32bit/file.exe


+ 0 - 0
Tests/RunCMake/find_program/32bit/file32bit.exe


+ 0 - 0
Tests/RunCMake/find_program/64bit/file.exe


+ 0 - 0
Tests/RunCMake/find_program/64bit/file64bit.exe


部分文件因为文件数量过多而无法显示