1 """distutils.msvc9compiler
2
3 Contains MSVCCompiler, an implementation of the abstract CCompiler class
4 for the Microsoft Visual Studio 2008.
5
6 The module is compatible with VS 2005 and VS 2008. You can find legacy support
7 for older versions of VS in distutils.msvccompiler.
8 """
9
10 # Written by Perry Stoll
11 # hacked by Robin Becker and Thomas Heller to do a better job of
12 # finding DevStudio (through the registry)
13 # ported to VS2005 and VS 2008 by Christian Heimes
14
15 import os
16 import subprocess
17 import sys
18 import re
19
20 from distutils.errors import DistutilsPlatformError
21 from distutils.ccompiler import CCompiler
22 from distutils import log
23
24 import winreg
25
26 RegOpenKeyEx = winreg.OpenKeyEx
27 RegEnumKey = winreg.EnumKey
28 RegEnumValue = winreg.EnumValue
29 RegError = winreg.error
30
31 HKEYS = (winreg.HKEY_USERS,
32 winreg.HKEY_CURRENT_USER,
33 winreg.HKEY_LOCAL_MACHINE,
34 winreg.HKEY_CLASSES_ROOT)
35
36 NATIVE_WIN64 = (sys.platform == 'win32' and sys.maxsize > 2**32)
37 if NATIVE_WIN64:
38 # Visual C++ is a 32-bit application, so we need to look in
39 # the corresponding registry branch, if we're running a
40 # 64-bit Python on Win64
41 VS_BASE = r"Software\Wow6432Node\Microsoft\VisualStudio\%0.1f"
42 WINSDK_BASE = r"Software\Wow6432Node\Microsoft\Microsoft SDKs\Windows"
43 NET_BASE = r"Software\Wow6432Node\Microsoft\.NETFramework"
44 else:
45 VS_BASE = r"Software\Microsoft\VisualStudio\%0.1f"
46 WINSDK_BASE = r"Software\Microsoft\Microsoft SDKs\Windows"
47 NET_BASE = r"Software\Microsoft\.NETFramework"
48
49 # A map keyed by get_platform() return values to values accepted by
50 # 'vcvarsall.bat'. Note a cross-compile may combine these (eg, 'x86_amd64' is
51 # the param to cross-compile on x86 targeting amd64.)
52 PLAT_TO_VCVARS = {
53 'win32' : 'x86',
54 'win-amd64' : 'amd64',
55 }
56
57 class ESC[4;38;5;81mReg:
58 """Helper class to read values from the registry
59 """
60
61 def get_value(cls, path, key):
62 for base in HKEYS:
63 d = cls.read_values(base, path)
64 if d and key in d:
65 return d[key]
66 raise KeyError(key)
67 get_value = classmethod(get_value)
68
69 def read_keys(cls, base, key):
70 """Return list of registry keys."""
71 try:
72 handle = RegOpenKeyEx(base, key)
73 except RegError:
74 return None
75 L = []
76 i = 0
77 while True:
78 try:
79 k = RegEnumKey(handle, i)
80 except RegError:
81 break
82 L.append(k)
83 i += 1
84 return L
85 read_keys = classmethod(read_keys)
86
87 def read_values(cls, base, key):
88 """Return dict of registry keys and values.
89
90 All names are converted to lowercase.
91 """
92 try:
93 handle = RegOpenKeyEx(base, key)
94 except RegError:
95 return None
96 d = {}
97 i = 0
98 while True:
99 try:
100 name, value, type = RegEnumValue(handle, i)
101 except RegError:
102 break
103 name = name.lower()
104 d[cls.convert_mbcs(name)] = cls.convert_mbcs(value)
105 i += 1
106 return d
107 read_values = classmethod(read_values)
108
109 def convert_mbcs(s):
110 dec = getattr(s, "decode", None)
111 if dec is not None:
112 try:
113 s = dec("mbcs")
114 except UnicodeError:
115 pass
116 return s
117 convert_mbcs = staticmethod(convert_mbcs)
118
119 class ESC[4;38;5;81mMacroExpander:
120
121 def __init__(self, version):
122 self.macros = {}
123 self.vsbase = VS_BASE % version
124 self.load_macros(version)
125
126 def set_macro(self, macro, path, key):
127 self.macros["$(%s)" % macro] = Reg.get_value(path, key)
128
129 def load_macros(self, version):
130 self.set_macro("VCInstallDir", self.vsbase + r"\Setup\VC", "productdir")
131 self.set_macro("VSInstallDir", self.vsbase + r"\Setup\VS", "productdir")
132 self.set_macro("FrameworkDir", NET_BASE, "installroot")
133 try:
134 if version >= 8.0:
135 self.set_macro("FrameworkSDKDir", NET_BASE,
136 "sdkinstallrootv2.0")
137 else:
138 raise KeyError("sdkinstallrootv2.0")
139 except KeyError:
140 raise DistutilsPlatformError(
141 """Python was built with Visual Studio 2008;
142 extensions must be built with a compiler than can generate compatible binaries.
143 Visual Studio 2008 was not found on this system. If you have Cygwin installed,
144 you can try compiling with MingW32, by passing "-c mingw32" to setup.py.""")
145
146 if version >= 9.0:
147 self.set_macro("FrameworkVersion", self.vsbase, "clr version")
148 self.set_macro("WindowsSdkDir", WINSDK_BASE, "currentinstallfolder")
149 else:
150 p = r"Software\Microsoft\NET Framework Setup\Product"
151 for base in HKEYS:
152 try:
153 h = RegOpenKeyEx(base, p)
154 except RegError:
155 continue
156 key = RegEnumKey(h, 0)
157 d = Reg.get_value(base, r"%s\%s" % (p, key))
158 self.macros["$(FrameworkVersion)"] = d["version"]
159
160 def sub(self, s):
161 for k, v in self.macros.items():
162 s = s.replace(k, v)
163 return s
164
165 def get_build_version():
166 """Return the version of MSVC that was used to build Python.
167
168 For Python 2.3 and up, the version number is included in
169 sys.version. For earlier versions, assume the compiler is MSVC 6.
170 """
171 prefix = "MSC v."
172 i = sys.version.find(prefix)
173 if i == -1:
174 return 6
175 i = i + len(prefix)
176 s, rest = sys.version[i:].split(" ", 1)
177 majorVersion = int(s[:-2]) - 6
178 if majorVersion >= 13:
179 # v13 was skipped and should be v14
180 majorVersion += 1
181 minorVersion = int(s[2:3]) / 10.0
182 # I don't think paths are affected by minor version in version 6
183 if majorVersion == 6:
184 minorVersion = 0
185 if majorVersion >= 6:
186 return majorVersion + minorVersion
187 # else we don't know what version of the compiler this is
188 return None
189
190 def normalize_and_reduce_paths(paths):
191 """Return a list of normalized paths with duplicates removed.
192
193 The current order of paths is maintained.
194 """
195 # Paths are normalized so things like: /a and /a/ aren't both preserved.
196 reduced_paths = []
197 for p in paths:
198 np = os.path.normpath(p)
199 # XXX(nnorwitz): O(n**2), if reduced_paths gets long perhaps use a set.
200 if np not in reduced_paths:
201 reduced_paths.append(np)
202 return reduced_paths
203
204 def removeDuplicates(variable):
205 """Remove duplicate values of an environment variable.
206 """
207 oldList = variable.split(os.pathsep)
208 newList = []
209 for i in oldList:
210 if i not in newList:
211 newList.append(i)
212 newVariable = os.pathsep.join(newList)
213 return newVariable
214
215 def find_vcvarsall(version):
216 """Find the vcvarsall.bat file
217
218 At first it tries to find the productdir of VS 2008 in the registry. If
219 that fails it falls back to the VS90COMNTOOLS env var.
220 """
221 vsbase = VS_BASE % version
222 try:
223 productdir = Reg.get_value(r"%s\Setup\VC" % vsbase,
224 "productdir")
225 except KeyError:
226 log.debug("Unable to find productdir in registry")
227 productdir = None
228
229 if not productdir or not os.path.isdir(productdir):
230 toolskey = "VS%0.f0COMNTOOLS" % version
231 toolsdir = os.environ.get(toolskey, None)
232
233 if toolsdir and os.path.isdir(toolsdir):
234 productdir = os.path.join(toolsdir, os.pardir, os.pardir, "VC")
235 productdir = os.path.abspath(productdir)
236 if not os.path.isdir(productdir):
237 log.debug("%s is not a valid directory" % productdir)
238 return None
239 else:
240 log.debug("Env var %s is not set or invalid" % toolskey)
241 if not productdir:
242 log.debug("No productdir found")
243 return None
244 vcvarsall = os.path.join(productdir, "vcvarsall.bat")
245 if os.path.isfile(vcvarsall):
246 return vcvarsall
247 log.debug("Unable to find vcvarsall.bat")
248 return None
249
250 def query_vcvarsall(version, arch="x86"):
251 """Launch vcvarsall.bat and read the settings from its environment
252 """
253 vcvarsall = find_vcvarsall(version)
254 interesting = {"include", "lib", "libpath", "path"}
255 result = {}
256
257 if vcvarsall is None:
258 raise DistutilsPlatformError("Unable to find vcvarsall.bat")
259 log.debug("Calling 'vcvarsall.bat %s' (version=%s)", arch, version)
260 popen = subprocess.Popen('"%s" %s & set' % (vcvarsall, arch),
261 stdout=subprocess.PIPE,
262 stderr=subprocess.PIPE)
263 try:
264 stdout, stderr = popen.communicate()
265 if popen.wait() != 0:
266 raise DistutilsPlatformError(stderr.decode("mbcs"))
267
268 stdout = stdout.decode("mbcs")
269 for line in stdout.split("\n"):
270 line = Reg.convert_mbcs(line)
271 if '=' not in line:
272 continue
273 line = line.strip()
274 key, value = line.split('=', 1)
275 key = key.lower()
276 if key in interesting:
277 if value.endswith(os.pathsep):
278 value = value[:-1]
279 result[key] = removeDuplicates(value)
280
281 finally:
282 popen.stdout.close()
283 popen.stderr.close()
284
285 if len(result) != len(interesting):
286 raise ValueError(str(list(result.keys())))
287
288 return result
289
290 # More globals
291 VERSION = get_build_version()
292 if VERSION < 8.0:
293 raise DistutilsPlatformError("VC %0.1f is not supported by this module" % VERSION)
294 # MACROS = MacroExpander(VERSION)
295
296 class ESC[4;38;5;81mMSVCCompiler(ESC[4;38;5;149mCCompiler) :
297 """Concrete class that implements an interface to Microsoft Visual C++,
298 as defined by the CCompiler abstract class."""
299
300 compiler_type = 'msvc'
301
302 # Just set this so CCompiler's constructor doesn't barf. We currently
303 # don't use the 'set_executables()' bureaucracy provided by CCompiler,
304 # as it really isn't necessary for this sort of single-compiler class.
305 # Would be nice to have a consistent interface with UnixCCompiler,
306 # though, so it's worth thinking about.
307 executables = {}
308
309 # Private class data (need to distinguish C from C++ source for compiler)
310 _c_extensions = ['.c']
311 _cpp_extensions = ['.cc', '.cpp', '.cxx']
312 _rc_extensions = ['.rc']
313 _mc_extensions = ['.mc']
314
315 # Needed for the filename generation methods provided by the
316 # base class, CCompiler.
317 src_extensions = (_c_extensions + _cpp_extensions +
318 _rc_extensions + _mc_extensions)
319 res_extension = '.res'
320 obj_extension = '.obj'
321 static_lib_extension = '.lib'
322 shared_lib_extension = '.dll'
323 static_lib_format = shared_lib_format = '%s%s'
324 exe_extension = '.exe'
325
326 def __init__(self, verbose=0, dry_run=0, force=0):
327 CCompiler.__init__ (self, verbose, dry_run, force)
328 self.__version = VERSION
329 self.__root = r"Software\Microsoft\VisualStudio"
330 # self.__macros = MACROS
331 self.__paths = []
332 # target platform (.plat_name is consistent with 'bdist')
333 self.plat_name = None
334 self.__arch = None # deprecated name
335 self.initialized = False
336
337 # -- Worker methods ------------------------------------------------
338
339 def manifest_setup_ldargs(self, output_filename, build_temp, ld_args):
340 # If we need a manifest at all, an embedded manifest is recommended.
341 # See MSDN article titled
342 # "How to: Embed a Manifest Inside a C/C++ Application"
343 # (currently at http://msdn2.microsoft.com/en-us/library/ms235591(VS.80).aspx)
344 # Ask the linker to generate the manifest in the temp dir, so
345 # we can check it, and possibly embed it, later.
346 temp_manifest = os.path.join(
347 build_temp,
348 os.path.basename(output_filename) + ".manifest")
349 ld_args.append('/MANIFESTFILE:' + temp_manifest)
350
351 def manifest_get_embed_info(self, target_desc, ld_args):
352 # If a manifest should be embedded, return a tuple of
353 # (manifest_filename, resource_id). Returns None if no manifest
354 # should be embedded. See http://bugs.python.org/issue7833 for why
355 # we want to avoid any manifest for extension modules if we can.
356 for arg in ld_args:
357 if arg.startswith("/MANIFESTFILE:"):
358 temp_manifest = arg.split(":", 1)[1]
359 break
360 else:
361 # no /MANIFESTFILE so nothing to do.
362 return None
363 if target_desc == CCompiler.EXECUTABLE:
364 # by default, executables always get the manifest with the
365 # CRT referenced.
366 mfid = 1
367 else:
368 # Extension modules try and avoid any manifest if possible.
369 mfid = 2
370 temp_manifest = self._remove_visual_c_ref(temp_manifest)
371 if temp_manifest is None:
372 return None
373 return temp_manifest, mfid
374
375 def _remove_visual_c_ref(self, manifest_file):
376 try:
377 # Remove references to the Visual C runtime, so they will
378 # fall through to the Visual C dependency of Python.exe.
379 # This way, when installed for a restricted user (e.g.
380 # runtimes are not in WinSxS folder, but in Python's own
381 # folder), the runtimes do not need to be in every folder
382 # with .pyd's.
383 # Returns either the filename of the modified manifest or
384 # None if no manifest should be embedded.
385 manifest_f = open(manifest_file)
386 try:
387 manifest_buf = manifest_f.read()
388 finally:
389 manifest_f.close()
390 pattern = re.compile(
391 r"""<assemblyIdentity.*?name=("|')Microsoft\."""\
392 r"""VC\d{2}\.CRT("|').*?(/>|</assemblyIdentity>)""",
393 re.DOTALL)
394 manifest_buf = re.sub(pattern, "", manifest_buf)
395 pattern = r"<dependentAssembly>\s*</dependentAssembly>"
396 manifest_buf = re.sub(pattern, "", manifest_buf)
397 # Now see if any other assemblies are referenced - if not, we
398 # don't want a manifest embedded.
399 pattern = re.compile(
400 r"""<assemblyIdentity.*?name=(?:"|')(.+?)(?:"|')"""
401 r""".*?(?:/>|</assemblyIdentity>)""", re.DOTALL)
402 if re.search(pattern, manifest_buf) is None:
403 return None
404
405 manifest_f = open(manifest_file, 'w')
406 try:
407 manifest_f.write(manifest_buf)
408 return manifest_file
409 finally:
410 manifest_f.close()
411 except OSError:
412 pass
413
414 # -- Miscellaneous methods -----------------------------------------
415
416 # Helper methods for using the MSVC registry settings
417
418 def find_exe(self, exe):
419 """Return path to an MSVC executable program.
420
421 Tries to find the program in several places: first, one of the
422 MSVC program search paths from the registry; next, the directories
423 in the PATH environment variable. If any of those work, return an
424 absolute path that is known to exist. If none of them work, just
425 return the original program name, 'exe'.
426 """
427 for p in self.__paths:
428 fn = os.path.join(os.path.abspath(p), exe)
429 if os.path.isfile(fn):
430 return fn
431
432 # didn't find it; try existing path
433 for p in os.environ['Path'].split(';'):
434 fn = os.path.join(os.path.abspath(p),exe)
435 if os.path.isfile(fn):
436 return fn
437
438 return exe