blob: a6cff2c04e7e162f795d30cc62f5428455d65fad [file] [log] [blame]
Christian Heimescbf3b5c2007-12-03 21:02:03 +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)
257 if popen.wait() != 0:
258 raise IOError(popen.stderr.read())
259
260 for line in popen.stdout:
261 line = Reg.convert_mbcs(line)
262 if '=' not in line:
263 continue
264 line = line.strip()
265 key, value = line.split('=')
266 key = key.lower()
267 if key in interesting:
268 if value.endswith(os.pathsep):
269 value = value[:-1]
270 result[key] = value
271
272 if len(result) != len(interesting):
273 raise ValueError(str(list(result.keys())))
274
275 return result
276
277# More globals
278VERSION = get_build_version()
279if VERSION < 8.0:
280 raise DistutilsPlatformError("VC %0.1f is not supported by this module" % VERSION)
281ARCH = get_build_architecture()
282# MACROS = MacroExpander(VERSION)
283VC_ENV = query_vcvarsall(VERSION, ARCH)
284
285class MSVCCompiler(CCompiler) :
286 """Concrete class that implements an interface to Microsoft Visual C++,
287 as defined by the CCompiler abstract class."""
288
289 compiler_type = 'msvc'
290
291 # Just set this so CCompiler's constructor doesn't barf. We currently
292 # don't use the 'set_executables()' bureaucracy provided by CCompiler,
293 # as it really isn't necessary for this sort of single-compiler class.
294 # Would be nice to have a consistent interface with UnixCCompiler,
295 # though, so it's worth thinking about.
296 executables = {}
297
298 # Private class data (need to distinguish C from C++ source for compiler)
299 _c_extensions = ['.c']
300 _cpp_extensions = ['.cc', '.cpp', '.cxx']
301 _rc_extensions = ['.rc']
302 _mc_extensions = ['.mc']
303
304 # Needed for the filename generation methods provided by the
305 # base class, CCompiler.
306 src_extensions = (_c_extensions + _cpp_extensions +
307 _rc_extensions + _mc_extensions)
308 res_extension = '.res'
309 obj_extension = '.obj'
310 static_lib_extension = '.lib'
311 shared_lib_extension = '.dll'
312 static_lib_format = shared_lib_format = '%s%s'
313 exe_extension = '.exe'
314
315 def __init__(self, verbose=0, dry_run=0, force=0):
316 CCompiler.__init__ (self, verbose, dry_run, force)
317 self.__version = VERSION
318 self.__arch = ARCH
319 self.__root = r"Software\Microsoft\VisualStudio"
320 # self.__macros = MACROS
321 self.__path = []
322 self.initialized = False
323
324 def initialize(self):
325 if "DISTUTILS_USE_SDK" in os.environ and "MSSdk" in os.environ and self.find_exe("cl.exe"):
326 # Assume that the SDK set up everything alright; don't try to be
327 # smarter
328 self.cc = "cl.exe"
329 self.linker = "link.exe"
330 self.lib = "lib.exe"
331 self.rc = "rc.exe"
332 self.mc = "mc.exe"
333 else:
334 self.__paths = VC_ENV['path'].split(os.pathsep)
335 os.environ['lib'] = VC_ENV['lib']
336 os.environ['include'] = VC_ENV['include']
337
338 if len(self.__paths) == 0:
339 raise DistutilsPlatformError("Python was built with %s, "
340 "and extensions need to be built with the same "
341 "version of the compiler, but it isn't installed."
342 % self.__product)
343
344 self.cc = self.find_exe("cl.exe")
345 self.linker = self.find_exe("link.exe")
346 self.lib = self.find_exe("lib.exe")
347 self.rc = self.find_exe("rc.exe") # resource compiler
348 self.mc = self.find_exe("mc.exe") # message compiler
349 #self.set_path_env_var('lib')
350 #self.set_path_env_var('include')
351
352 # extend the MSVC path with the current path
353 try:
354 for p in os.environ['path'].split(';'):
355 self.__paths.append(p)
356 except KeyError:
357 pass
358 self.__paths = normalize_and_reduce_paths(self.__paths)
359 os.environ['path'] = ";".join(self.__paths)
360
361 self.preprocess_options = None
362 if self.__arch == "x86":
363 self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3',
364 '/DNDEBUG']
365 self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3',
366 '/Z7', '/D_DEBUG']
367 else:
368 # Win64
369 self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', '/GS-' ,
370 '/DNDEBUG']
371 self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/GS-',
372 '/Z7', '/D_DEBUG']
373
374 self.ldflags_shared = ['/DLL', '/nologo', '/INCREMENTAL:NO']
375 if self.__version >= 7:
376 self.ldflags_shared_debug = [
377 '/DLL', '/nologo', '/INCREMENTAL:no', '/DEBUG', '/pdb:None'
378 ]
379 self.ldflags_static = [ '/nologo']
380
381 self.initialized = True
382
383 # -- Worker methods ------------------------------------------------
384
385 def object_filenames(self,
386 source_filenames,
387 strip_dir=0,
388 output_dir=''):
389 # Copied from ccompiler.py, extended to return .res as 'object'-file
390 # for .rc input file
391 if output_dir is None: output_dir = ''
392 obj_names = []
393 for src_name in source_filenames:
394 (base, ext) = os.path.splitext (src_name)
395 base = os.path.splitdrive(base)[1] # Chop off the drive
396 base = base[os.path.isabs(base):] # If abs, chop off leading /
397 if ext not in self.src_extensions:
398 # Better to raise an exception instead of silently continuing
399 # and later complain about sources and targets having
400 # different lengths
401 raise CompileError ("Don't know how to compile %s" % src_name)
402 if strip_dir:
403 base = os.path.basename (base)
404 if ext in self._rc_extensions:
405 obj_names.append (os.path.join (output_dir,
406 base + self.res_extension))
407 elif ext in self._mc_extensions:
408 obj_names.append (os.path.join (output_dir,
409 base + self.res_extension))
410 else:
411 obj_names.append (os.path.join (output_dir,
412 base + self.obj_extension))
413 return obj_names
414
415
416 def compile(self, sources,
417 output_dir=None, macros=None, include_dirs=None, debug=0,
418 extra_preargs=None, extra_postargs=None, depends=None):
419
420 if not self.initialized:
421 self.initialize()
422 compile_info = self._setup_compile(output_dir, macros, include_dirs,
423 sources, depends, extra_postargs)
424 macros, objects, extra_postargs, pp_opts, build = compile_info
425
426 compile_opts = extra_preargs or []
427 compile_opts.append ('/c')
428 if debug:
429 compile_opts.extend(self.compile_options_debug)
430 else:
431 compile_opts.extend(self.compile_options)
432
433 for obj in objects:
434 try:
435 src, ext = build[obj]
436 except KeyError:
437 continue
438 if debug:
439 # pass the full pathname to MSVC in debug mode,
440 # this allows the debugger to find the source file
441 # without asking the user to browse for it
442 src = os.path.abspath(src)
443
444 if ext in self._c_extensions:
445 input_opt = "/Tc" + src
446 elif ext in self._cpp_extensions:
447 input_opt = "/Tp" + src
448 elif ext in self._rc_extensions:
449 # compile .RC to .RES file
450 input_opt = src
451 output_opt = "/fo" + obj
452 try:
453 self.spawn([self.rc] + pp_opts +
454 [output_opt] + [input_opt])
455 except DistutilsExecError as msg:
456 raise CompileError(msg)
457 continue
458 elif ext in self._mc_extensions:
459 # Compile .MC to .RC file to .RES file.
460 # * '-h dir' specifies the directory for the
461 # generated include file
462 # * '-r dir' specifies the target directory of the
463 # generated RC file and the binary message resource
464 # it includes
465 #
466 # For now (since there are no options to change this),
467 # we use the source-directory for the include file and
468 # the build directory for the RC file and message
469 # resources. This works at least for win32all.
470 h_dir = os.path.dirname(src)
471 rc_dir = os.path.dirname(obj)
472 try:
473 # first compile .MC to .RC and .H file
474 self.spawn([self.mc] +
475 ['-h', h_dir, '-r', rc_dir] + [src])
476 base, _ = os.path.splitext (os.path.basename (src))
477 rc_file = os.path.join (rc_dir, base + '.rc')
478 # then compile .RC to .RES file
479 self.spawn([self.rc] +
480 ["/fo" + obj] + [rc_file])
481
482 except DistutilsExecError as msg:
483 raise CompileError(msg)
484 continue
485 else:
486 # how to handle this file?
487 raise CompileError("Don't know how to compile %s to %s"
488 % (src, obj))
489
490 output_opt = "/Fo" + obj
491 try:
492 self.spawn([self.cc] + compile_opts + pp_opts +
493 [input_opt, output_opt] +
494 extra_postargs)
495 except DistutilsExecError as msg:
496 raise CompileError(msg)
497
498 return objects
499
500
501 def create_static_lib(self,
502 objects,
503 output_libname,
504 output_dir=None,
505 debug=0,
506 target_lang=None):
507
508 if not self.initialized:
509 self.initialize()
510 (objects, output_dir) = self._fix_object_args(objects, output_dir)
511 output_filename = self.library_filename(output_libname,
512 output_dir=output_dir)
513
514 if self._need_link(objects, output_filename):
515 lib_args = objects + ['/OUT:' + output_filename]
516 if debug:
517 pass # XXX what goes here?
518 try:
519 self.spawn([self.lib] + lib_args)
520 except DistutilsExecError as msg:
521 raise LibError(msg)
522 else:
523 log.debug("skipping %s (up-to-date)", output_filename)
524
525
526 def link(self,
527 target_desc,
528 objects,
529 output_filename,
530 output_dir=None,
531 libraries=None,
532 library_dirs=None,
533 runtime_library_dirs=None,
534 export_symbols=None,
535 debug=0,
536 extra_preargs=None,
537 extra_postargs=None,
538 build_temp=None,
539 target_lang=None):
540
541 if not self.initialized:
542 self.initialize()
543 (objects, output_dir) = self._fix_object_args(objects, output_dir)
544 fixed_args = self._fix_lib_args(libraries, library_dirs,
545 runtime_library_dirs)
546 (libraries, library_dirs, runtime_library_dirs) = fixed_args
547
548 if runtime_library_dirs:
549 self.warn ("I don't know what to do with 'runtime_library_dirs': "
550 + str (runtime_library_dirs))
551
552 lib_opts = gen_lib_options(self,
553 library_dirs, runtime_library_dirs,
554 libraries)
555 if output_dir is not None:
556 output_filename = os.path.join(output_dir, output_filename)
557
558 if self._need_link(objects, output_filename):
559 if target_desc == CCompiler.EXECUTABLE:
560 if debug:
561 ldflags = self.ldflags_shared_debug[1:]
562 else:
563 ldflags = self.ldflags_shared[1:]
564 else:
565 if debug:
566 ldflags = self.ldflags_shared_debug
567 else:
568 ldflags = self.ldflags_shared
569
570 export_opts = []
571 for sym in (export_symbols or []):
572 export_opts.append("/EXPORT:" + sym)
573
574 ld_args = (ldflags + lib_opts + export_opts +
575 objects + ['/OUT:' + output_filename])
576
577 # The MSVC linker generates .lib and .exp files, which cannot be
578 # suppressed by any linker switches. The .lib files may even be
579 # needed! Make sure they are generated in the temporary build
580 # directory. Since they have different names for debug and release
581 # builds, they can go into the same directory.
582 if export_symbols is not None:
583 (dll_name, dll_ext) = os.path.splitext(
584 os.path.basename(output_filename))
585 implib_file = os.path.join(
586 os.path.dirname(objects[0]),
587 self.library_filename(dll_name))
588 ld_args.append ('/IMPLIB:' + implib_file)
589
590 if extra_preargs:
591 ld_args[:0] = extra_preargs
592 if extra_postargs:
593 ld_args.extend(extra_postargs)
594
595 self.mkpath(os.path.dirname(output_filename))
596 try:
597 self.spawn([self.linker] + ld_args)
598 except DistutilsExecError as msg:
599 raise LinkError(msg)
600
601 else:
602 log.debug("skipping %s (up-to-date)", output_filename)
603
604
605 # -- Miscellaneous methods -----------------------------------------
606 # These are all used by the 'gen_lib_options() function, in
607 # ccompiler.py.
608
609 def library_dir_option(self, dir):
610 return "/LIBPATH:" + dir
611
612 def runtime_library_dir_option(self, dir):
613 raise DistutilsPlatformError(
614 "don't know how to set runtime library search path for MSVC++")
615
616 def library_option(self, lib):
617 return self.library_filename(lib)
618
619
620 def find_library_file(self, dirs, lib, debug=0):
621 # Prefer a debugging library if found (and requested), but deal
622 # with it if we don't have one.
623 if debug:
624 try_names = [lib + "_d", lib]
625 else:
626 try_names = [lib]
627 for dir in dirs:
628 for name in try_names:
629 libfile = os.path.join(dir, self.library_filename (name))
630 if os.path.exists(libfile):
631 return libfile
632 else:
633 # Oops, didn't find it in *any* of 'dirs'
634 return None
635
636 # Helper methods for using the MSVC registry settings
637
638 def find_exe(self, exe):
639 """Return path to an MSVC executable program.
640
641 Tries to find the program in several places: first, one of the
642 MSVC program search paths from the registry; next, the directories
643 in the PATH environment variable. If any of those work, return an
644 absolute path that is known to exist. If none of them work, just
645 return the original program name, 'exe'.
646 """
647 for p in self.__paths:
648 fn = os.path.join(os.path.abspath(p), exe)
649 if os.path.isfile(fn):
650 return fn
651
652 # didn't find it; try existing path
653 for p in os.environ['Path'].split(';'):
654 fn = os.path.join(os.path.abspath(p),exe)
655 if os.path.isfile(fn):
656 return fn
657
658 return exe