Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
allendowney
GitHub Repository: allendowney/cpython
Path: blob/main/Tools/c-analyzer/distutils/msvccompiler.py
12 views
1
"""distutils.msvccompiler
2
3
Contains MSVCCompiler, an implementation of the abstract CCompiler class
4
for the Microsoft Visual Studio.
5
"""
6
7
# Written by Perry Stoll
8
# hacked by Robin Becker and Thomas Heller to do a better job of
9
# finding DevStudio (through the registry)
10
11
import sys, os
12
from distutils.errors import DistutilsPlatformError
13
from distutils.ccompiler import CCompiler
14
from distutils import log
15
16
_can_read_reg = False
17
try:
18
import winreg
19
20
_can_read_reg = True
21
hkey_mod = winreg
22
23
RegOpenKeyEx = winreg.OpenKeyEx
24
RegEnumKey = winreg.EnumKey
25
RegEnumValue = winreg.EnumValue
26
RegError = winreg.error
27
28
except ImportError:
29
try:
30
import win32api
31
import win32con
32
_can_read_reg = True
33
hkey_mod = win32con
34
35
RegOpenKeyEx = win32api.RegOpenKeyEx
36
RegEnumKey = win32api.RegEnumKey
37
RegEnumValue = win32api.RegEnumValue
38
RegError = win32api.error
39
except ImportError:
40
log.info("Warning: Can't read registry to find the "
41
"necessary compiler setting\n"
42
"Make sure that Python modules winreg, "
43
"win32api or win32con are installed.")
44
45
if _can_read_reg:
46
HKEYS = (hkey_mod.HKEY_USERS,
47
hkey_mod.HKEY_CURRENT_USER,
48
hkey_mod.HKEY_LOCAL_MACHINE,
49
hkey_mod.HKEY_CLASSES_ROOT)
50
51
def read_keys(base, key):
52
"""Return list of registry keys."""
53
try:
54
handle = RegOpenKeyEx(base, key)
55
except RegError:
56
return None
57
L = []
58
i = 0
59
while True:
60
try:
61
k = RegEnumKey(handle, i)
62
except RegError:
63
break
64
L.append(k)
65
i += 1
66
return L
67
68
def read_values(base, key):
69
"""Return dict of registry keys and values.
70
71
All names are converted to lowercase.
72
"""
73
try:
74
handle = RegOpenKeyEx(base, key)
75
except RegError:
76
return None
77
d = {}
78
i = 0
79
while True:
80
try:
81
name, value, type = RegEnumValue(handle, i)
82
except RegError:
83
break
84
name = name.lower()
85
d[convert_mbcs(name)] = convert_mbcs(value)
86
i += 1
87
return d
88
89
def convert_mbcs(s):
90
dec = getattr(s, "decode", None)
91
if dec is not None:
92
try:
93
s = dec("mbcs")
94
except UnicodeError:
95
pass
96
return s
97
98
class MacroExpander:
99
def __init__(self, version):
100
self.macros = {}
101
self.load_macros(version)
102
103
def set_macro(self, macro, path, key):
104
for base in HKEYS:
105
d = read_values(base, path)
106
if d:
107
self.macros["$(%s)" % macro] = d[key]
108
break
109
110
def load_macros(self, version):
111
vsbase = r"Software\Microsoft\VisualStudio\%0.1f" % version
112
self.set_macro("VCInstallDir", vsbase + r"\Setup\VC", "productdir")
113
self.set_macro("VSInstallDir", vsbase + r"\Setup\VS", "productdir")
114
net = r"Software\Microsoft\.NETFramework"
115
self.set_macro("FrameworkDir", net, "installroot")
116
try:
117
if version > 7.0:
118
self.set_macro("FrameworkSDKDir", net, "sdkinstallrootv1.1")
119
else:
120
self.set_macro("FrameworkSDKDir", net, "sdkinstallroot")
121
except KeyError as exc: #
122
raise DistutilsPlatformError(
123
"""Python was built with Visual Studio 2003;
124
extensions must be built with a compiler than can generate compatible binaries.
125
Visual Studio 2003 was not found on this system. If you have Cygwin installed,
126
you can try compiling with MingW32, by passing "-c mingw32" to setup.py.""")
127
128
p = r"Software\Microsoft\NET Framework Setup\Product"
129
for base in HKEYS:
130
try:
131
h = RegOpenKeyEx(base, p)
132
except RegError:
133
continue
134
key = RegEnumKey(h, 0)
135
d = read_values(base, r"%s\%s" % (p, key))
136
self.macros["$(FrameworkVersion)"] = d["version"]
137
138
def sub(self, s):
139
for k, v in self.macros.items():
140
s = s.replace(k, v)
141
return s
142
143
def get_build_version():
144
"""Return the version of MSVC that was used to build Python.
145
146
For Python 2.3 and up, the version number is included in
147
sys.version. For earlier versions, assume the compiler is MSVC 6.
148
"""
149
prefix = "MSC v."
150
i = sys.version.find(prefix)
151
if i == -1:
152
return 6
153
i = i + len(prefix)
154
s, rest = sys.version[i:].split(" ", 1)
155
majorVersion = int(s[:-2]) - 6
156
if majorVersion >= 13:
157
# v13 was skipped and should be v14
158
majorVersion += 1
159
minorVersion = int(s[2:3]) / 10.0
160
# I don't think paths are affected by minor version in version 6
161
if majorVersion == 6:
162
minorVersion = 0
163
if majorVersion >= 6:
164
return majorVersion + minorVersion
165
# else we don't know what version of the compiler this is
166
return None
167
168
def get_build_architecture():
169
"""Return the processor architecture.
170
171
Possible results are "Intel" or "AMD64".
172
"""
173
174
prefix = " bit ("
175
i = sys.version.find(prefix)
176
if i == -1:
177
return "Intel"
178
j = sys.version.find(")", i)
179
return sys.version[i+len(prefix):j]
180
181
def normalize_and_reduce_paths(paths):
182
"""Return a list of normalized paths with duplicates removed.
183
184
The current order of paths is maintained.
185
"""
186
# Paths are normalized so things like: /a and /a/ aren't both preserved.
187
reduced_paths = []
188
for p in paths:
189
np = os.path.normpath(p)
190
# XXX(nnorwitz): O(n**2), if reduced_paths gets long perhaps use a set.
191
if np not in reduced_paths:
192
reduced_paths.append(np)
193
return reduced_paths
194
195
196
class MSVCCompiler(CCompiler) :
197
"""Concrete class that implements an interface to Microsoft Visual C++,
198
as defined by the CCompiler abstract class."""
199
200
compiler_type = 'msvc'
201
202
# Just set this so CCompiler's constructor doesn't barf. We currently
203
# don't use the 'set_executables()' bureaucracy provided by CCompiler,
204
# as it really isn't necessary for this sort of single-compiler class.
205
# Would be nice to have a consistent interface with UnixCCompiler,
206
# though, so it's worth thinking about.
207
executables = {}
208
209
# Private class data (need to distinguish C from C++ source for compiler)
210
_c_extensions = ['.c']
211
_cpp_extensions = ['.cc', '.cpp', '.cxx']
212
_rc_extensions = ['.rc']
213
_mc_extensions = ['.mc']
214
215
# Needed for the filename generation methods provided by the
216
# base class, CCompiler.
217
src_extensions = (_c_extensions + _cpp_extensions +
218
_rc_extensions + _mc_extensions)
219
res_extension = '.res'
220
obj_extension = '.obj'
221
static_lib_extension = '.lib'
222
shared_lib_extension = '.dll'
223
static_lib_format = shared_lib_format = '%s%s'
224
exe_extension = '.exe'
225
226
def __init__(self, verbose=0, dry_run=0, force=0):
227
CCompiler.__init__ (self, verbose, dry_run, force)
228
self.__version = get_build_version()
229
self.__arch = get_build_architecture()
230
if self.__arch == "Intel":
231
# x86
232
if self.__version >= 7:
233
self.__root = r"Software\Microsoft\VisualStudio"
234
self.__macros = MacroExpander(self.__version)
235
else:
236
self.__root = r"Software\Microsoft\Devstudio"
237
self.__product = "Visual Studio version %s" % self.__version
238
else:
239
# Win64. Assume this was built with the platform SDK
240
self.__product = "Microsoft SDK compiler %s" % (self.__version + 6)
241
242
self.initialized = False
243
244
245
# -- Miscellaneous methods -----------------------------------------
246
247
# Helper methods for using the MSVC registry settings
248
249
def find_exe(self, exe):
250
"""Return path to an MSVC executable program.
251
252
Tries to find the program in several places: first, one of the
253
MSVC program search paths from the registry; next, the directories
254
in the PATH environment variable. If any of those work, return an
255
absolute path that is known to exist. If none of them work, just
256
return the original program name, 'exe'.
257
"""
258
for p in self.__paths:
259
fn = os.path.join(os.path.abspath(p), exe)
260
if os.path.isfile(fn):
261
return fn
262
263
# didn't find it; try existing path
264
for p in os.environ['Path'].split(';'):
265
fn = os.path.join(os.path.abspath(p),exe)
266
if os.path.isfile(fn):
267
return fn
268
269
return exe
270
271
def get_msvc_paths(self, path, platform='x86'):
272
"""Get a list of devstudio directories (include, lib or path).
273
274
Return a list of strings. The list will be empty if unable to
275
access the registry or appropriate registry keys not found.
276
"""
277
if not _can_read_reg:
278
return []
279
280
path = path + " dirs"
281
if self.__version >= 7:
282
key = (r"%s\%0.1f\VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories"
283
% (self.__root, self.__version))
284
else:
285
key = (r"%s\6.0\Build System\Components\Platforms"
286
r"\Win32 (%s)\Directories" % (self.__root, platform))
287
288
for base in HKEYS:
289
d = read_values(base, key)
290
if d:
291
if self.__version >= 7:
292
return self.__macros.sub(d[path]).split(";")
293
else:
294
return d[path].split(";")
295
# MSVC 6 seems to create the registry entries we need only when
296
# the GUI is run.
297
if self.__version == 6:
298
for base in HKEYS:
299
if read_values(base, r"%s\6.0" % self.__root) is not None:
300
self.warn("It seems you have Visual Studio 6 installed, "
301
"but the expected registry settings are not present.\n"
302
"You must at least run the Visual Studio GUI once "
303
"so that these entries are created.")
304
break
305
return []
306
307
def set_path_env_var(self, name):
308
"""Set environment variable 'name' to an MSVC path type value.
309
310
This is equivalent to a SET command prior to execution of spawned
311
commands.
312
"""
313
314
if name == "lib":
315
p = self.get_msvc_paths("library")
316
else:
317
p = self.get_msvc_paths(name)
318
if p:
319
os.environ[name] = ';'.join(p)
320
321
322
if get_build_version() >= 8.0:
323
log.debug("Importing new compiler from distutils.msvc9compiler")
324
OldMSVCCompiler = MSVCCompiler
325
from distutils.msvc9compiler import MSVCCompiler
326
# get_build_architecture not really relevant now we support cross-compile
327
from distutils.msvc9compiler import MacroExpander
328
329