Bug 1364137 - Get both 32-bit and 64-bit registry values when searching for the Windows SDK. r=glandium, a=NPOTB
authorBob Owen <bobowencode@gmail.com>
Sat, 22 Jul 2017 11:32:24 +0100
changeset 412066 c2635cb28a2fb7de2bb03e85a3706cbe78f196b5
parent 412065 436dee8a429bc210409a2af030d24c186a8d6eac
child 412067 293c71a18bc783fec69f6b925b8954e12344e728
push id7540
push userryanvm@gmail.com
push dateMon, 24 Jul 2017 12:29:10 +0000
treeherdermozilla-beta@f3b0db0e8b2d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersglandium, NPOTB
bugs1364137
milestone55.0
Bug 1364137 - Get both 32-bit and 64-bit registry values when searching for the Windows SDK. r=glandium, a=NPOTB 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
@@ -219,103 +219,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.