blob: 828d7fbf7a153df6311273d7117682dcfc7675eb [file] [log] [blame]
Christian Heimes3305c522007-12-03 13:47:29 +00001"""distutils.msvc9compiler
2
3Contains MSVCCompiler, an implementation of the abstract CCompiler class
4for the Microsoft Visual Studio 2008.
5
6The module is compatible with VS 2005 and VS 2008. You can find legacy support
7for 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__revision__ = "$Id$"
16
17import os
18import subprocess
19import sys
20from distutils.errors import (DistutilsExecError, DistutilsPlatformError,
21 CompileError, LibError, LinkError)
22from distutils.ccompiler import (CCompiler, gen_preprocess_options,
23 gen_lib_options)
24from distutils import log
25
26import _winreg
27
28RegOpenKeyEx = _winreg.OpenKeyEx
29RegEnumKey = _winreg.EnumKey
30RegEnumValue = _winreg.EnumValue
31RegError = _winreg.error
32
33HKEYS = (_winreg.HKEY_USERS,
34 _winreg.HKEY_CURRENT_USER,
35 _winreg.HKEY_LOCAL_MACHINE,
36 _winreg.HKEY_CLASSES_ROOT)
37
38VS_BASE = r"Software\Microsoft\VisualStudio\%0.1f"
39WINSDK_BASE = r"Software\Microsoft\Microsoft SDKs\Windows"
40NET_BASE = r"Software\Microsoft\.NETFramework"
41ARCHS = {'DEFAULT' : 'x86',
42 'intel' : 'x86', 'x86' : 'x86',
43 'amd64' : 'x64', 'x64' : 'x64',
44 'itanium' : 'ia64', 'ia64' : 'ia64',
45 }
46
47# The globals VERSION, ARCH, MACROS and VC_ENV are defined later
48
49class Reg:
50 """Helper class to read values from the registry
51 """
52
53 @classmethod
54 def get_value(cls, path, key):
55 for base in HKEYS:
56 d = cls.read_values(base, path)
57 if d and key in d:
58 return d[key]
59 raise KeyError(key)
60
61 @classmethod
62 def read_keys(cls, base, key):
63 """Return list of registry keys."""
64 try:
65 handle = RegOpenKeyEx(base, key)
66 except RegError:
67 return None
68 L = []
69 i = 0
70 while True:
71 try:
72 k = RegEnumKey(handle, i)
73 except RegError:
74 break
75 L.append(k)
76 i += 1
77 return L
78
79 @classmethod
80 def read_values(cls, base, key):
81 """Return dict of registry keys and values.
82
83 All names are converted to lowercase.
84 """
85 try:
86 handle = RegOpenKeyEx(base, key)
87 except RegError:
88 return None
89 d = {}
90 i = 0
91 while True:
92 try:
93 name, value, type = RegEnumValue(handle, i)
94 except RegError:
95 break
96 name = name.lower()
97 d[cls.convert_mbcs(name)] = cls.convert_mbcs(value)
98 i += 1
99 return d
100
101 @staticmethod
102 def convert_mbcs(s):
103 dec = getattr(s, "decode", None)
104 if dec is not None:
105 try:
106 s = dec("mbcs")
107 except UnicodeError:
108 pass
109 return s
110
111class MacroExpander:
112
113 def __init__(self, version):
114 self.macros = {}
115 self.vsbase = VS_BASE % version
116 self.load_macros(version)
117
118 def set_macro(self, macro, path, key):
119 self.macros["$(%s)" % macro] = Reg.get_value(path, key)
120
121 def load_macros(self, version):
122 self.set_macro("VCInstallDir", self.vsbase + r"\Setup\VC", "productdir")
123 self.set_macro("VSInstallDir", self.vsbase + r"\Setup\VS", "productdir")
124 self.set_macro("FrameworkDir", NET_BASE, "installroot")
125 try:
126 if version >= 8.0:
127 self.set_macro("FrameworkSDKDir", NET_BASE,
128 "sdkinstallrootv2.0")
129 else:
130 raise KeyError("sdkinstallrootv2.0")
131 except KeyError as exc: #
132 raise DistutilsPlatformError(
133 """Python was built with Visual Studio 2008;
134extensions must be built with a compiler than can generate compatible binaries.
135Visual Studio 2008 was not found on this system. If you have Cygwin installed,
136you can try compiling with MingW32, by passing "-c mingw32" to setup.py.""")
137
138 if version >= 9.0:
139 self.set_macro("FrameworkVersion", self.vsbase, "clr version")
140 self.set_macro("WindowsSdkDir", WINSDK_BASE, "currentinstallfolder")
141 else:
142 p = r"Software\Microsoft\NET Framework Setup\Product"
143 for base in HKEYS:
144 try:
145 h = RegOpenKeyEx(base, p)
146 except RegError:
147 continue
148 key = RegEnumKey(h, 0)
149 d = Reg.get_value(base, r"%s\%s" % (p, key))
150 self.macros["$(FrameworkVersion)"] = d["version"]
151
152 def sub(self, s):
153 for k, v in self.macros.items():
154 s = s.replace(k, v)
155 return s
156
157def get_build_version():
158 """Return the version of MSVC that was used to build Python.
159
160 For Python 2.3 and up, the version number is included in
161 sys.version. For earlier versions, assume the compiler is MSVC 6.
162 """
163 prefix = "MSC v."
164 i = sys.version.find(prefix)
165 if i == -1:
166 return 6
167 i = i + len(prefix)
168 s, rest = sys.version[i:].split(" ", 1)
169 majorVersion = int(s[:-2]) - 6
170 minorVersion = int(s[2:3]) / 10.0
171 # I don't think paths are affected by minor version in version 6
172 if majorVersion == 6:
173 minorVersion = 0
174 if majorVersion >= 6:
175 return majorVersion + minorVersion
176 # else we don't know what version of the compiler this is
177 return None
178
179def get_build_architecture():
180 """Return the processor architecture.
181
182 Possible results are "x86" or "amd64".
183 """
184 prefix = " bit ("
185 i = sys.version.find(prefix)
186 if i == -1:
187 return "x86"
188 j = sys.version.find(")", i)
189 sysarch = sys.version[i+len(prefix):j].lower()
190 arch = ARCHS.get(sysarch, None)
191 if arch is None:
192 return ARCHS['DEFAULT']
193 else:
194 return arch
195
196def normalize_and_reduce_paths(paths):
197 """Return a list of normalized paths with duplicates removed.
198
199 The current order of paths is maintained.
200 """
201 # Paths are normalized so things like: /a and /a/ aren't both preserved.
202 reduced_paths = []
203 for p in paths:
204 np = os.path.normpath(p)
205 # XXX(nnorwitz): O(n**2), if reduced_paths gets long perhaps use a set.
206 if np not in reduced_paths:
207 reduced_paths.append(np)
208 return reduced_paths
209
210def find_vcvarsall(version):
211 """Find the vcvarsall.bat file
212
213 At first it tries to find the productdir of VS 2008 in the registry. If
214 that fails it falls back to the VS90COMNTOOLS env var.
215 """
216 vsbase = VS_BASE % version
217 try:
218 productdir = Reg.get_value(r"%s\Setup\VC" % vsbase,
219 "productdir")
220 except KeyError:
221 log.debug("Unable to find productdir in registry")
222 productdir = None
223
224 if not productdir or not os.path.isdir(productdir):
225 toolskey = "VS%0.f0COMNTOOLS" % version
226 toolsdir = os.environ.get(toolskey, None)
227
228 if toolsdir and os.path.isdir(toolsdir):
229 productdir = os.path.join(toolsdir, os.pardir, os.pardir, "VC")
230 productdir = os.path.abspath(productdir)
231 if not os.path.isdir(productdir):
232 log.debug("%s is not a valid directory" % productdir)
233 return None
234 else:
235 log.debug("Env var %s is not set or invalid" % toolskey)
236 if not productdir:
237 log.debug("No productdir found")
238 return None
239 vcvarsall = os.path.join(productdir, "vcvarsall.bat")
240 if os.path.isfile(vcvarsall):
241 return vcvarsall
242 log.debug("Unable to find vcvarsall.bat")
243 return None
244
245def query_vcvarsall(version, arch="x86"):
246 """Launch vcvarsall.bat and read the settings from its environment
247 """
248 vcvarsall = find_vcvarsall(version)
249 interesting = set(("include", "lib", "libpath", "path"))
250 result = {}
251
252 if vcvarsall is None:
253 raise IOError("Unable to find vcvarsall.bat")
254 popen = subprocess.Popen('"%s" %s & set' % (vcvarsall, arch),
255 stdout=subprocess.PIPE,
256 stderr=subprocess.PIPE)
Christian Heimes3305c522007-12-03 13:47:29 +0000257
Christian Heimescbdb7052007-12-05 20:10:38 +0000258 stdout, stderr = popen.communicate()
259 if popen.wait() != 0:
260 raise IOError(stderr.decode("mbcs"))
261
262 stdout = stdout.decode("mbcs")
263 for line in stdout.split("\n"):
Christian Heimes3305c522007-12-03 13:47:29 +0000264 line = Reg.convert_mbcs(line)
265 if '=' not in line:
266 continue
267 line = line.strip()
268 key, value = line.split('=')
269 key = key.lower()
270 if key in interesting:
271 if value.endswith(os.pathsep):
272 value = value[:-1]
273 result[key] = value
274
275 if len(result) != len(interesting):
276 raise ValueError(str(list(result.keys())))
277
278 return result
279
280# More globals
281VERSION = get_build_version()
282if VERSION < 8.0:
283 raise DistutilsPlatformError("VC %0.1f is not supported by this module" % VERSION)
284ARCH = get_build_architecture()
285# MACROS = MacroExpander(VERSION)
286VC_ENV = query_vcvarsall(VERSION, ARCH)
287
288class MSVCCompiler(CCompiler) :
289 """Concrete class that implements an interface to Microsoft Visual C++,
290 as defined by the CCompiler abstract class."""
291
292 compiler_type = 'msvc'
293
294 # Just set this so CCompiler's constructor doesn't barf. We currently
295 # don't use the 'set_executables()' bureaucracy provided by CCompiler,
296 # as it really isn't necessary for this sort of single-compiler class.
297 # Would be nice to have a consistent interface with UnixCCompiler,
298 # though, so it's worth thinking about.
299 executables = {}
300
301 # Private class data (need to distinguish C from C++ source for compiler)
302 _c_extensions = ['.c']
303 _cpp_extensions = ['.cc', '.cpp', '.cxx']
304 _rc_extensions = ['.rc']
305 _mc_extensions = ['.mc']
306
307 # Needed for the filename generation methods provided by the
308 # base class, CCompiler.
309 src_extensions = (_c_extensions + _cpp_extensions +
310 _rc_extensions + _mc_extensions)
311 res_extension = '.res'
312 obj_extension = '.obj'
313 static_lib_extension = '.lib'
314 shared_lib_extension = '.dll'
315 static_lib_format = shared_lib_format = '%s%s'
316 exe_extension = '.exe'
317
318 def __init__(self, verbose=0, dry_run=0, force=0):
319 CCompiler.__init__ (self, verbose, dry_run, force)
320 self.__version = VERSION
321 self.__arch = ARCH
322 self.__root = r"Software\Microsoft\VisualStudio"
323 # self.__macros = MACROS
324 self.__path = []
325 self.initialized = False
326
327 def initialize(self):
328 if "DISTUTILS_USE_SDK" in os.environ and "MSSdk" in os.environ and self.find_exe("cl.exe"):
329 # Assume that the SDK set up everything alright; don't try to be
330 # smarter
331 self.cc = "cl.exe"
332 self.linker = "link.exe"
333 self.lib = "lib.exe"
334 self.rc = "rc.exe"
335 self.mc = "mc.exe"
336 else:
337 self.__paths = VC_ENV['path'].split(os.pathsep)
338 os.environ['lib'] = VC_ENV['lib']
339 os.environ['include'] = VC_ENV['include']
340
341 if len(self.__paths) == 0:
342 raise DistutilsPlatformError("Python was built with %s, "
343 "and extensions need to be built with the same "
344 "version of the compiler, but it isn't installed."
345 % self.__product)
346
347 self.cc = self.find_exe("cl.exe")
348 self.linker = self.find_exe("link.exe")
349 self.lib = self.find_exe("lib.exe")
350 self.rc = self.find_exe("rc.exe") # resource compiler
351 self.mc = self.find_exe("mc.exe") # message compiler
352 #self.set_path_env_var('lib')
353 #self.set_path_env_var('include')
354
355 # extend the MSVC path with the current path
356 try:
357 for p in os.environ['path'].split(';'):
358 self.__paths.append(p)
359 except KeyError:
360 pass
361 self.__paths = normalize_and_reduce_paths(self.__paths)
362 os.environ['path'] = ";".join(self.__paths)
363
364 self.preprocess_options = None
365 if self.__arch == "x86":
366 self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3',
367 '/DNDEBUG']
368 self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3',
369 '/Z7', '/D_DEBUG']
370 else:
371 # Win64
372 self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', '/GS-' ,
373 '/DNDEBUG']
374 self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/GS-',
375 '/Z7', '/D_DEBUG']
376
377 self.ldflags_shared = ['/DLL', '/nologo', '/INCREMENTAL:NO']
378 if self.__version >= 7:
379 self.ldflags_shared_debug = [
380 '/DLL', '/nologo', '/INCREMENTAL:no', '/DEBUG', '/pdb:None'
381 ]
382 self.ldflags_static = [ '/nologo']
383
384 self.initialized = True
385
386 # -- Worker methods ------------------------------------------------
387
388 def object_filenames(self,
389 source_filenames,
390 strip_dir=0,
391 output_dir=''):
392 # Copied from ccompiler.py, extended to return .res as 'object'-file
393 # for .rc input file
394 if output_dir is None: output_dir = ''
395 obj_names = []
396 for src_name in source_filenames:
397 (base, ext) = os.path.splitext (src_name)
398 base = os.path.splitdrive(base)[1] # Chop off the drive
399 base = base[os.path.isabs(base):] # If abs, chop off leading /
400 if ext not in self.src_extensions:
401 # Better to raise an exception instead of silently continuing
402 # and later complain about sources and targets having
403 # different lengths
404 raise CompileError ("Don't know how to compile %s" % src_name)
405 if strip_dir:
406 base = os.path.basename (base)
407 if ext in self._rc_extensions:
408 obj_names.append (os.path.join (output_dir,
409 base + self.res_extension))
410 elif ext in self._mc_extensions:
411 obj_names.append (os.path.join (output_dir,
412 base + self.res_extension))
413 else:
414 obj_names.append (os.path.join (output_dir,
415 base + self.obj_extension))
416 return obj_names
417
418
419 def compile(self, sources,
420 output_dir=None, macros=None, include_dirs=None, debug=0,
421 extra_preargs=None, extra_postargs=None, depends=None):
422
423 if not self.initialized:
424 self.initialize()
425 compile_info = self._setup_compile(output_dir, macros, include_dirs,
426 sources, depends, extra_postargs)
427 macros, objects, extra_postargs, pp_opts, build = compile_info
428
429 compile_opts = extra_preargs or []
430 compile_opts.append ('/c')
431 if debug:
432 compile_opts.extend(self.compile_options_debug)
433 else:
434 compile_opts.extend(self.compile_options)
435
436 for obj in objects:
437 try:
438 src, ext = build[obj]
439 except KeyError:
440 continue
441 if debug:
442 # pass the full pathname to MSVC in debug mode,
443 # this allows the debugger to find the source file
444 # without asking the user to browse for it
445 src = os.path.abspath(src)
446
447 if ext in self._c_extensions:
448 input_opt = "/Tc" + src
449 elif ext in self._cpp_extensions:
450 input_opt = "/Tp" + src
451 elif ext in self._rc_extensions:
452 # compile .RC to .RES file
453 input_opt = src
454 output_opt = "/fo" + obj
455 try:
456 self.spawn([self.rc] + pp_opts +
457 [output_opt] + [input_opt])
458 except DistutilsExecError as msg:
459 raise CompileError(msg)
460 continue
461 elif ext in self._mc_extensions:
462 # Compile .MC to .RC file to .RES file.
463 # * '-h dir' specifies the directory for the
464 # generated include file
465 # * '-r dir' specifies the target directory of the
466 # generated RC file and the binary message resource
467 # it includes
468 #
469 # For now (since there are no options to change this),
470 # we use the source-directory for the include file and
471 # the build directory for the RC file and message
472 # resources. This works at least for win32all.
473 h_dir = os.path.dirname(src)
474 rc_dir = os.path.dirname(obj)
475 try:
476 # first compile .MC to .RC and .H file
477 self.spawn([self.mc] +
478 ['-h', h_dir, '-r', rc_dir] + [src])
479 base, _ = os.path.splitext (os.path.basename (src))
480 rc_file = os.path.join (rc_dir, base + '.rc')
481 # then compile .RC to .RES file
482 self.spawn([self.rc] +
483 ["/fo" + obj] + [rc_file])
484
485 except DistutilsExecError as msg:
486 raise CompileError(msg)
487 continue
488 else:
489 # how to handle this file?
490 raise CompileError("Don't know how to compile %s to %s"
491 % (src, obj))
492
493 output_opt = "/Fo" + obj
494 try:
495 self.spawn([self.cc] + compile_opts + pp_opts +
496 [input_opt, output_opt] +
497 extra_postargs)
498 except DistutilsExecError as msg:
499 raise CompileError(msg)
500
501 return objects
502
503
504 def create_static_lib(self,
505 objects,
506 output_libname,
507 output_dir=None,
508 debug=0,
509 target_lang=None):
510
511 if not self.initialized:
512 self.initialize()
513 (objects, output_dir) = self._fix_object_args(objects, output_dir)
514 output_filename = self.library_filename(output_libname,
515 output_dir=output_dir)
516
517 if self._need_link(objects, output_filename):
518 lib_args = objects + ['/OUT:' + output_filename]
519 if debug:
520 pass # XXX what goes here?
521 try:
522 self.spawn([self.lib] + lib_args)
523 except DistutilsExecError as msg:
524 raise LibError(msg)
525 else:
526 log.debug("skipping %s (up-to-date)", output_filename)
527
528
529 def link(self,
530 target_desc,
531 objects,
532 output_filename,
533 output_dir=None,
534 libraries=None,
535 library_dirs=None,
536 runtime_library_dirs=None,
537 export_symbols=None,
538 debug=0,
539 extra_preargs=None,
540 extra_postargs=None,
541 build_temp=None,
542 target_lang=None):
543
544 if not self.initialized:
545 self.initialize()
546 (objects, output_dir) = self._fix_object_args(objects, output_dir)
547 fixed_args = self._fix_lib_args(libraries, library_dirs,
548 runtime_library_dirs)
549 (libraries, library_dirs, runtime_library_dirs) = fixed_args
550
551 if runtime_library_dirs:
552 self.warn ("I don't know what to do with 'runtime_library_dirs': "
553 + str (runtime_library_dirs))
554
555 lib_opts = gen_lib_options(self,
556 library_dirs, runtime_library_dirs,
557 libraries)
558 if output_dir is not None:
559 output_filename = os.path.join(output_dir, output_filename)
560
561 if self._need_link(objects, output_filename):
562 if target_desc == CCompiler.EXECUTABLE:
563 if debug:
564 ldflags = self.ldflags_shared_debug[1:]
565 else:
566 ldflags = self.ldflags_shared[1:]
567 else:
568 if debug:
569 ldflags = self.ldflags_shared_debug
570 else:
571 ldflags = self.ldflags_shared
572
573 export_opts = []
574 for sym in (export_symbols or []):
575 export_opts.append("/EXPORT:" + sym)
576
577 ld_args = (ldflags + lib_opts + export_opts +
578 objects + ['/OUT:' + output_filename])
579
580 # The MSVC linker generates .lib and .exp files, which cannot be
581 # suppressed by any linker switches. The .lib files may even be
582 # needed! Make sure they are generated in the temporary build
583 # directory. Since they have different names for debug and release
584 # builds, they can go into the same directory.
585 if export_symbols is not None:
586 (dll_name, dll_ext) = os.path.splitext(
587 os.path.basename(output_filename))
588 implib_file = os.path.join(
589 os.path.dirname(objects[0]),
590 self.library_filename(dll_name))
591 ld_args.append ('/IMPLIB:' + implib_file)
592
593 if extra_preargs:
594 ld_args[:0] = extra_preargs
595 if extra_postargs:
596 ld_args.extend(extra_postargs)
597
598 self.mkpath(os.path.dirname(output_filename))
599 try:
600 self.spawn([self.linker] + ld_args)
601 except DistutilsExecError as msg:
602 raise LinkError(msg)
603
604 else:
605 log.debug("skipping %s (up-to-date)", output_filename)
606
607
608 # -- Miscellaneous methods -----------------------------------------
609 # These are all used by the 'gen_lib_options() function, in
610 # ccompiler.py.
611
612 def library_dir_option(self, dir):
613 return "/LIBPATH:" + dir
614
615 def runtime_library_dir_option(self, dir):
616 raise DistutilsPlatformError(
617 "don't know how to set runtime library search path for MSVC++")
618
619 def library_option(self, lib):
620 return self.library_filename(lib)
621
622
623 def find_library_file(self, dirs, lib, debug=0):
624 # Prefer a debugging library if found (and requested), but deal
625 # with it if we don't have one.
626 if debug:
627 try_names = [lib + "_d", lib]
628 else:
629 try_names = [lib]
630 for dir in dirs:
631 for name in try_names:
632 libfile = os.path.join(dir, self.library_filename (name))
633 if os.path.exists(libfile):
634 return libfile
635 else:
636 # Oops, didn't find it in *any* of 'dirs'
637 return None
638
639 # Helper methods for using the MSVC registry settings
640
641 def find_exe(self, exe):
642 """Return path to an MSVC executable program.
643
644 Tries to find the program in several places: first, one of the
645 MSVC program search paths from the registry; next, the directories
646 in the PATH environment variable. If any of those work, return an
647 absolute path that is known to exist. If none of them work, just
648 return the original program name, 'exe'.
649 """
650 for p in self.__paths:
651 fn = os.path.join(os.path.abspath(p), exe)
652 if os.path.isfile(fn):
653 return fn
654
655 # didn't find it; try existing path
656 for p in os.environ['Path'].split(';'):
657 fn = os.path.join(os.path.abspath(p),exe)
658 if os.path.isfile(fn):
659 return fn
660
661 return exe