Skip to content

Commit 8fe702a

Browse files
committed
Refactor registry search
Use context manager for closing registries
1 parent 0078d76 commit 8fe702a

File tree

1 file changed

+73
-55
lines changed

1 file changed

+73
-55
lines changed

setup.py

Lines changed: 73 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
an egg or wheel.
77
"""
88

9+
import collections
910
import fnmatch
1011
import glob
1112
import os
@@ -24,83 +25,100 @@
2425
DEVTOOLS = "MsDev" if sys.platform == "win32" else "Mono"
2526
ARCH = "x64" if platform.architecture()[0] == "64bit" else "x86"
2627

28+
###############################################################################
29+
# Windows Keys Constants for MSBUILD tools
30+
RegKey = collections.namedtuple('RegKey', 'sdk_name key value_name suffix')
31+
vs_python = "Programs\\Common\\Microsoft\\Visual C++ for Python\\9.0\\WinSDK"
32+
vs_root = "SOFTWARE\\Microsoft\\MSBuild\\ToolsVersions\\{0}"
33+
sdks_root = "SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\v{0}Win32Tools"
34+
kits_root = "SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots"
35+
kits_suffix = os.path.join("bin", ARCH)
2736

37+
WIN_SDK_KEYS = (
38+
RegKey(sdk_name="Windows Kit 10.0", key=kits_root,
39+
value_name="KitsRoot10", suffix=kits_suffix),
40+
41+
RegKey(sdk_name="Windows Kit 8.1", key=kits_root,
42+
value_name="KitsRoot81", suffix=kits_suffix),
43+
44+
RegKey(sdk_name="Windows Kit 8.0", key=kits_root,
45+
value_name="KitsRoot", suffix=kits_suffix),
46+
47+
RegKey(sdk_name="Windows SDK 7.1A", key=sdks_root.format("7.1A\\WinSDK-"),
48+
value_name="InstallationFolder", suffix=""),
49+
50+
RegKey(sdk_name="Windows SDK 7.1", key=sdks_root.format("7.1\\WinSDK"),
51+
value_name="InstallationFolder", suffix=""),
52+
53+
RegKey(sdk_name="Windows SDK 7.0A", key=sdks_root.format("7.0A\\WinSDK-"),
54+
value_name="InstallationFolder", suffix=""),
55+
56+
RegKey(sdk_name="Windows SDK 7.0", key=sdks_root.format("7.0\\WinSDK"),
57+
value_name="InstallationFolder", suffix=""),
58+
59+
RegKey(sdk_name="Windows SDK 6.0A", key=sdks_root.format("6.0A\\WinSDK"),
60+
value_name="InstallationFolder", suffix=""),
61+
)
62+
63+
VS_KEYS = (
64+
RegKey(sdk_name="MSBuild 14", key=vs_root.format("14.0"),
65+
value_name="MSBuildToolsPath", suffix=""),
66+
67+
RegKey(sdk_name="MSBuild 12", key=vs_root.format("12.0"),
68+
value_name="MSBuildToolsPath", suffix=""),
69+
70+
RegKey(sdk_name="MSBuild 4", key=vs_root.format("4.0"),
71+
value_name="MSBuildToolsPath", suffix=""),
72+
73+
RegKey(sdk_name="MSBuild 3.5", key=vs_root.format("3.5"),
74+
value_name="MSBuildToolsPath", suffix=""),
75+
76+
RegKey(sdk_name="MSBuild 2.0", key=vs_root.format("2.0"),
77+
value_name="MSBuildToolsPath", suffix=""),
78+
)
79+
80+
81+
###############################################################################
2882
def _find_msbuild_tool(tool="msbuild.exe", use_windows_sdk=False):
2983
"""Return full path to one of the Microsoft build tools"""
84+
# Search in PATH first
3085
path = spawn.find_executable(tool)
3186
if path:
3287
return path
3388

89+
# Search within registry to find build tools
3490
try: # PY2
3591
import _winreg as winreg
3692
except ImportError: # PY3
3793
import winreg
3894

39-
keys_to_check = []
40-
if use_windows_sdk:
41-
sdks_root = r"SOFTWARE\Microsoft\Microsoft SDKs\Windows"
42-
kits_root = r"SOFTWARE\Microsoft\Windows Kits\Installed Roots"
43-
kits_suffix = os.path.join("bin", ARCH)
44-
keys_to_check.extend([
45-
("Windows Kit 10.0", kits_root, "KitsRoot10", kits_suffix),
46-
("Windows Kit 8.1", kits_root, "KitsRoot81", kits_suffix),
47-
("Windows Kit 8.0", kits_root, "KitsRoot", kits_suffix),
48-
("Windows SDK 7.1A", sdks_root + r"\v7.1A\WinSDK-Win32Tools", "InstallationFolder"),
49-
("Windows SDK 7.1", sdks_root + r"\v7.1\WinSDKWin32Tools", "InstallationFolder"),
50-
("Windows SDK 7.0A", sdks_root + r"\v7.0A\WinSDK-Win32Tools", "InstallationFolder"),
51-
("Windows SDK 7.0", sdks_root + r"\v7.0\WinSDKWin32Tools", "InstallationFolder"),
52-
("Windows SDK 6.0A", sdks_root + r"\v6.0A\WinSDKWin32Tools", "InstallationFolder")
53-
])
54-
else:
55-
vs_root = r"SOFTWARE\Microsoft\MSBuild\ToolsVersions"
56-
keys_to_check.extend([
57-
("MSBuild 14", vs_root + r"\14.0", "MSBuildToolsPath"),
58-
("MSBuild 12", vs_root + r"\12.0", "MSBuildToolsPath"),
59-
("MSBuild 4", vs_root + r"\4.0", "MSBuildToolsPath"),
60-
("MSBuild 3.5", vs_root + r"\3.5", "MSBuildToolsPath"),
61-
("MSBuild 2.0", vs_root + r"\2.0", "MSBuildToolsPath")
62-
])
63-
64-
# read the possible tools paths from the various registry locations
65-
paths_to_check = []
66-
hreg = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE)
67-
try:
68-
for key_to_check in keys_to_check:
69-
sdk_name, key, value_name = key_to_check[:3]
70-
suffix = key_to_check[3] if len(key_to_check) > 3 else None
71-
hkey = None
72-
try:
73-
hkey = winreg.OpenKey(hreg, key)
74-
val, type_ = winreg.QueryValueEx(hkey, value_name)
95+
keys_to_check = WIN_SDK_KEYS if use_windows_sdk else VS_KEYS
96+
for rkey in keys_to_check:
97+
try:
98+
with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, rkey.key) as hkey:
99+
val, type_ = winreg.QueryValueEx(hkey, rkey.value_name)
75100
if type_ != winreg.REG_SZ:
76101
continue
77-
if suffix:
78-
val = os.path.join(val, suffix)
79-
paths_to_check.append((sdk_name, val))
80-
except WindowsError:
81-
pass
82-
finally:
83-
if hkey:
84-
hkey.Close()
85-
finally:
86-
hreg.Close()
102+
path = os.path.join(val, rkey.suffix, tool)
103+
if os.path.exists(path):
104+
log.info("Using {0} from {1}".format(tool, rkey.sdk_name))
105+
return path
106+
except WindowsError:
107+
# Key doesn't exist
108+
pass
87109

88110
# Add Visual C++ for Python as a fall-back in case one
89111
# of the other Windows SDKs isn't installed
90112
if use_windows_sdk:
113+
sdk_name = "Visual C++ for Python"
91114
localappdata = os.environ["LOCALAPPDATA"]
92-
pywinsdk = localappdata + r"\Programs\Common\Microsoft\Visual C++ for Python\9.0\WinSDK\Bin"
93-
if ARCH == "x64":
94-
pywinsdk += r"\x64"
95-
paths_to_check.append(("Visual C++ for Python", pywinsdk))
96-
97-
for sdk_name, path in paths_to_check:
98-
path = os.path.join(path, tool)
115+
suffix = "Bin\\x64" if ARCH == "x64" else "Bin"
116+
path = os.path.join(localappdata, vs_python, suffix, tool)
99117
if os.path.exists(path):
100-
log.info("Using %s from %s" % (tool, sdk_name))
118+
log.info("Using {0} from {1}".format(tool, sdk_name))
101119
return path
102120

103-
raise RuntimeError("%s could not be found" % tool)
121+
raise RuntimeError("{0} could not be found".format(tool))
104122

105123

106124
class BuildExtPythonnet(build_ext.build_ext):

0 commit comments

Comments
 (0)