Bug 1364137: Get both 32-bit and 64-bit registry values when searching for the Windows SDK. r=glandium
authorBob Owen <bobowencode@gmail.com>
Sat, 22 Jul 2017 11:32:24 +0100
changeset 419107 2626221a97cc902ec1ca9c4e81bbc368fe1eb383
parent 419106 bf96c2e5614817945b2e5d8ff19c4c85a75c2c11
child 419108 1173ebb5a3f073e3dc8e157d7e20fe010a4ebbb0
push id7566
push usermtabara@mozilla.com
push dateWed, 02 Aug 2017 08:25:16 +0000
treeherdermozilla-beta@86913f512c3c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersglandium
bugs1364137
milestone56.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1364137: Get both 32-bit and 64-bit registry values when searching for the Windows SDK. r=glandium The values that we need to find in the registry can be inconsistent across different installations, so we retrieve values from both views in our search for a valid SDK. This also ensures this works for 32-bit and 64-bit python.
build/moz.configure/util.configure
build/moz.configure/windows.configure
--- a/build/moz.configure/util.configure
+++ b/build/moz.configure/util.configure
@@ -236,103 +236,124 @@ def unique_list(l):
 # Windows.
 # The `pattern` argument is a string starting with HKEY_ and giving the full
 # "path" of the registry key to get the value for, with backslash separators.
 # The string can contains wildcards ('*').
 # The result of this functions is an enumerator yielding tuples for each
 # match. Each of these tuples contains the key name matching wildcards
 # followed by the value.
 #
+# The `get_32_and_64_bit` argument is a boolean, if True then it will return the
+# values from the 32-bit and 64-bit registry views. This defaults to False,
+# which will return the view depending on the bitness of python.
+#
 # Examples:
 #   get_registry_values(r'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\'
 #                       r'Windows Kits\Installed Roots\KitsRoot*')
 #   yields e.g.:
 #     ('KitsRoot81', r'C:\Program Files (x86)\Windows Kits\8.1\')
 #     ('KitsRoot10', r'C:\Program Files (x86)\Windows Kits\10\')
 #
 #   get_registry_values(r'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\'
 #                       r'Windows Kits\Installed Roots\KitsRoot8.1')
 #   yields e.g.:
 #     (r'C:\Program Files (x86)\Windows Kits\8.1\',)
 #
 #   get_registry_values(r'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\'
+#                       r'Windows Kits\Installed Roots\KitsRoot8.1',
+#                       get_32_and_64_bit=True)
+#   yields e.g.:
+#     (r'C:\Program Files (x86)\Windows Kits\8.1\',)
+#     (r'C:\Program Files\Windows Kits\8.1\',)
+#
+#   get_registry_values(r'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\'
 #                       r'Windows Kits\*\KitsRoot*')
 #   yields e.g.:
 #     ('Installed Roots', 'KitsRoot81',
 #      r'C:\Program Files (x86)\Windows Kits\8.1\')
 #     ('Installed Roots', 'KitsRoot10',
 #      r'C:\Program Files (x86)\Windows Kits\10\')
 #
 #   get_registry_values(r'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\'
 #                       r'VisualStudio\VC\*\x86\*\Compiler')
 #   yields e.g.:
 #     ('19.0', 'arm', r'C:\...\amd64_arm\cl.exe')
 #     ('19.0', 'x64', r'C:\...\amd64\cl.exe')
 #     ('19.0', 'x86', r'C:\...\amd64_x86\cl.exe')
 @imports(_import='_winreg', _as='winreg')
 @imports(_from='__builtin__', _import='WindowsError')
 @imports(_from='fnmatch', _import='fnmatch')
-def get_registry_values(pattern):
+def get_registry_values(pattern, get_32_and_64_bit=False):
     def enum_helper(func, key):
         i = 0
         while True:
             try:
                 yield func(key, i)
             except WindowsError:
                 break
             i += 1
 
-    def get_keys(key, pattern):
+    def get_keys(key, pattern, access_mask):
         try:
-            s = winreg.OpenKey(key, '\\'.join(pattern[:-1]))
+            s = winreg.OpenKey(key, '\\'.join(pattern[:-1]), 0, access_mask)
         except WindowsError:
             return
         for k in enum_helper(winreg.EnumKey, s):
             if fnmatch(k, pattern[-1]):
                 try:
-                    yield k, winreg.OpenKey(s, k)
+                    yield k, winreg.OpenKey(s, k, 0, access_mask)
                 except WindowsError:
                     pass
 
-    def get_values(key, pattern):
+    def get_values(key, pattern, access_mask):
         try:
-            s = winreg.OpenKey(key, '\\'.join(pattern[:-1]))
+            s = winreg.OpenKey(key, '\\'.join(pattern[:-1]), 0, access_mask)
         except WindowsError:
             return
         for k, v, t in enum_helper(winreg.EnumValue, s):
             if fnmatch(k, pattern[-1]):
                 yield k, v
 
     def split_pattern(pattern):
         subpattern = []
         for p in pattern:
             subpattern.append(p)
             if '*' in p:
                 yield subpattern
                 subpattern = []
         if subpattern:
             yield subpattern
 
+    def get_all_values(keys, pattern, access_mask):
+        for i, p in enumerate(pattern):
+            next_keys = []
+            for base_key in keys:
+                matches = base_key[:-1]
+                base_key = base_key[-1]
+                if i == len(pattern) - 1:
+                    want_name = '*' in p[-1]
+                    for name, value in get_values(base_key, p, access_mask):
+                        yield matches + ((name, value) if want_name else (value,))
+                else:
+                    for name, k in get_keys(base_key, p, access_mask):
+                        next_keys.append(matches + (name, k))
+            keys = next_keys
+
     pattern = pattern.split('\\')
     assert pattern[0].startswith('HKEY_')
     keys = [(getattr(winreg, pattern[0]),)]
     pattern = list(split_pattern(pattern[1:]))
-    for i, p in enumerate(pattern):
-        next_keys = []
-        for base_key in keys:
-            matches = base_key[:-1]
-            base_key = base_key[-1]
-            if i == len(pattern) - 1:
-                want_name = '*' in p[-1]
-                for name, value in get_values(base_key, p):
-                    yield matches + ((name, value) if want_name else (value,))
-            else:
-                for name, k in get_keys(base_key, p):
-                    next_keys.append(matches + (name, k))
-        keys = next_keys
+    if get_32_and_64_bit:
+        for match in get_all_values(keys, pattern, winreg.KEY_READ | winreg.KEY_WOW64_32KEY):
+            yield match
+        for match in get_all_values(keys, pattern, winreg.KEY_READ | winreg.KEY_WOW64_64KEY):
+            yield match
+    else:
+        for match in get_all_values(keys, pattern, winreg.KEY_READ):
+            yield match
 
 
 @imports(_from='mozbuild.configure.util', _import='Version', _as='_Version')
 def Version(v):
     'A version number that can be compared usefully.'
     return _Version(v)
 
 # Denotes a deprecated option. Combines option() and @depends:
--- a/build/moz.configure/windows.configure
+++ b/build/moz.configure/windows.configure
@@ -28,19 +28,19 @@ option(env='WINDOWSSDKDIR', nargs=1,
 
 @depends('WINDOWSSDKDIR', host)
 def windows_sdk_dir(value, host):
     if value:
         return value
     if host.kernel != 'WINNT':
         return ()
 
-    return tuple(x[1] for x in get_registry_values(
+    return set(x[1] for x in get_registry_values(
         r'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Kits\Installed Roots'
-        r'\KitsRoot*'))
+        r'\KitsRoot*', get_32_and_64_bit=True))
 
 # The Windows SDK 8.1 and 10 have different layouts. The former has
 # $SDK/include/$subdir, while the latter has $SDK/include/$version/$subdir.
 # The vcvars* scripts don't actually care about the version, they just take
 # the last alphanumerically.
 # The $SDK/lib directories always have version subdirectories, but while the
 # versions match the one in $SDK/include for SDK 10, it's "winv6.3" for SDK
 # 8.1.