Commit 8d7e80cf authored by Marc Chevrier's avatar Marc Chevrier 🌴
Browse files

find_* commands: add control over Windows registry views

Fixes: #22775
parent 08941a9a
......@@ -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.
......
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.
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``
......@@ -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`+
......
......@@ -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|
......
......@@ -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|
......
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``
......
......@@ -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|
......
......@@ -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::
......
......@@ -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>
......
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
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.
......@@ -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 {
......
......@@ -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)
......
......@@ -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;
......
......@@ -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,
......
......@@ -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;
......
......@@ -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
......
......@@ -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)
......
......@@ -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
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment