1 """distutils.msvccompiler
3 Contains MSVCCompiler, an implementation of the abstract CCompiler class
4 for the Microsoft Visual Studio.
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)
11 # This module should be kept compatible with Python 2.1.
13 __revision__ = "$Id: msvccompiler.py 54645 2007-04-01 18:29:47Z neal.norwitz $"
15 import sys, os, string
16 from distutils.errors import \
17 DistutilsExecError, DistutilsPlatformError, \
18 CompileError, LibError, LinkError
19 from distutils.ccompiler import \
20 CCompiler, gen_preprocess_options, gen_lib_options
21 from distutils import log
30 RegOpenKeyEx = _winreg.OpenKeyEx
31 RegEnumKey = _winreg.EnumKey
32 RegEnumValue = _winreg.EnumValue
33 RegError = _winreg.error
42 RegOpenKeyEx = win32api.RegOpenKeyEx
43 RegEnumKey = win32api.RegEnumKey
44 RegEnumValue = win32api.RegEnumValue
45 RegError = win32api.error
48 log.info("Warning: Can't read registry to find the "
49 "necessary compiler setting\n"
50 "Make sure that Python modules _winreg, "
51 "win32api or win32con are installed.")
55 HKEYS = (hkey_mod.HKEY_USERS,
56 hkey_mod.HKEY_CURRENT_USER,
57 hkey_mod.HKEY_LOCAL_MACHINE,
58 hkey_mod.HKEY_CLASSES_ROOT)
60 def read_keys(base, key):
61 """Return list of registry keys."""
64 handle = RegOpenKeyEx(base, key)
71 k = RegEnumKey(handle, i)
78 def read_values(base, key):
79 """Return dict of registry keys and values.
81 All names are converted to lowercase.
84 handle = RegOpenKeyEx(base, key)
91 name, value, type = RegEnumValue(handle, i)
95 d[convert_mbcs(name)] = convert_mbcs(value)
100 enc = getattr(s, "encode", None)
110 def __init__(self, version):
112 self.load_macros(version)
114 def set_macro(self, macro, path, key):
116 d = read_values(base, path)
118 self.macros["$(%s)" % macro] = d[key]
121 def load_macros(self, version):
122 vsbase = r"Software\Microsoft\VisualStudio\%0.1f" % version
123 self.set_macro("VCInstallDir", vsbase + r"\Setup\VC", "productdir")
124 self.set_macro("VSInstallDir", vsbase + r"\Setup\VS", "productdir")
125 net = r"Software\Microsoft\.NETFramework"
126 self.set_macro("FrameworkDir", net, "installroot")
129 self.set_macro("FrameworkSDKDir", net, "sdkinstallrootv1.1")
131 self.set_macro("FrameworkSDKDir", net, "sdkinstallroot")
132 except KeyError, exc: #
133 raise DistutilsPlatformError, \
134 ("""Python was built with Visual Studio 2003;
135 extensions must be built with a compiler than can generate compatible binaries.
136 Visual Studio 2003 was not found on this system. If you have Cygwin installed,
137 you can try compiling with MingW32, by passing "-c mingw32" to setup.py.""")
139 p = r"Software\Microsoft\NET Framework Setup\Product"
142 h = RegOpenKeyEx(base, p)
145 key = RegEnumKey(h, 0)
146 d = read_values(base, r"%s\%s" % (p, key))
147 self.macros["$(FrameworkVersion)"] = d["version"]
150 for k, v in self.macros.items():
151 s = string.replace(s, k, v)
154 def get_build_version():
155 """Return the version of MSVC that was used to build Python.
157 For Python 2.3 and up, the version number is included in
158 sys.version. For earlier versions, assume the compiler is MSVC 6.
162 i = string.find(sys.version, prefix)
166 s, rest = sys.version[i:].split(" ", 1)
167 majorVersion = int(s[:-2]) - 6
168 minorVersion = int(s[2:3]) / 10.0
169 # I don't think paths are affected by minor version in version 6
170 if majorVersion == 6:
172 if majorVersion >= 6:
173 return majorVersion + minorVersion
174 # else we don't know what version of the compiler this is
177 def get_build_architecture():
178 """Return the processor architecture.
180 Possible results are "Intel", "Itanium", or "AMD64".
184 i = string.find(sys.version, prefix)
187 j = string.find(sys.version, ")", i)
188 return sys.version[i+len(prefix):j]
190 def normalize_and_reduce_paths(paths):
191 """Return a list of normalized paths with duplicates removed.
193 The current order of paths is maintained.
195 # Paths are normalized so things like: /a and /a/ aren't both preserved.
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)
205 class MSVCCompiler (CCompiler) :
206 """Concrete class that implements an interface to Microsoft Visual C++,
207 as defined by the CCompiler abstract class."""
209 compiler_type = 'msvc'
211 # Just set this so CCompiler's constructor doesn't barf. We currently
212 # don't use the 'set_executables()' bureaucracy provided by CCompiler,
213 # as it really isn't necessary for this sort of single-compiler class.
214 # Would be nice to have a consistent interface with UnixCCompiler,
215 # though, so it's worth thinking about.
218 # Private class data (need to distinguish C from C++ source for compiler)
219 _c_extensions = ['.c']
220 _cpp_extensions = ['.cc', '.cpp', '.cxx']
221 _rc_extensions = ['.rc']
222 _mc_extensions = ['.mc']
224 # Needed for the filename generation methods provided by the
225 # base class, CCompiler.
226 src_extensions = (_c_extensions + _cpp_extensions +
227 _rc_extensions + _mc_extensions)
228 res_extension = '.res'
229 obj_extension = '.obj'
230 static_lib_extension = '.lib'
231 shared_lib_extension = '.dll'
232 static_lib_format = shared_lib_format = '%s%s'
233 exe_extension = '.exe'
235 def __init__ (self, verbose=0, dry_run=0, force=0):
236 CCompiler.__init__ (self, verbose, dry_run, force)
237 self.__version = get_build_version()
238 self.__arch = get_build_architecture()
239 if self.__arch == "Intel":
241 if self.__version >= 7:
242 self.__root = r"Software\Microsoft\VisualStudio"
243 self.__macros = MacroExpander(self.__version)
245 self.__root = r"Software\Microsoft\Devstudio"
246 self.__product = "Visual Studio version %s" % self.__version
248 # Win64. Assume this was built with the platform SDK
249 self.__product = "Microsoft SDK compiler %s" % (self.__version + 6)
251 self.initialized = False
253 def initialize(self):
255 if os.environ.has_key("DISTUTILS_USE_SDK") and os.environ.has_key("MSSdk") and self.find_exe("cl.exe"):
256 # Assume that the SDK set up everything alright; don't try to be
259 self.linker = "link.exe"
264 self.__paths = self.get_msvc_paths("path")
266 if len (self.__paths) == 0:
267 raise DistutilsPlatformError, \
268 ("Python was built with %s, "
269 "and extensions need to be built with the same "
270 "version of the compiler, but it isn't installed." % self.__product)
272 self.cc = self.find_exe("cl.exe")
273 self.linker = self.find_exe("link.exe")
274 self.lib = self.find_exe("lib.exe")
275 self.rc = self.find_exe("rc.exe") # resource compiler
276 self.mc = self.find_exe("mc.exe") # message compiler
277 self.set_path_env_var('lib')
278 self.set_path_env_var('include')
280 # extend the MSVC path with the current path
282 for p in string.split(os.environ['path'], ';'):
283 self.__paths.append(p)
286 self.__paths = normalize_and_reduce_paths(self.__paths)
287 os.environ['path'] = string.join(self.__paths, ';')
289 self.preprocess_options = None
290 if self.__arch == "Intel":
291 self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', '/GX' ,
293 self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/GX',
297 self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', '/GS-' ,
299 self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/GS-',
302 self.ldflags_shared = ['/DLL', '/nologo', '/INCREMENTAL:NO']
303 if self.__version >= 7:
304 self.ldflags_shared_debug = [
305 '/DLL', '/nologo', '/INCREMENTAL:no', '/DEBUG'
308 self.ldflags_shared_debug = [
309 '/DLL', '/nologo', '/INCREMENTAL:no', '/pdb:None', '/DEBUG'
311 self.ldflags_static = [ '/nologo']
313 self.initialized = True
315 # -- Worker methods ------------------------------------------------
317 def object_filenames (self,
321 # Copied from ccompiler.py, extended to return .res as 'object'-file
323 if output_dir is None: output_dir = ''
325 for src_name in source_filenames:
326 (base, ext) = os.path.splitext (src_name)
327 base = os.path.splitdrive(base)[1] # Chop off the drive
328 base = base[os.path.isabs(base):] # If abs, chop off leading /
329 if ext not in self.src_extensions:
330 # Better to raise an exception instead of silently continuing
331 # and later complain about sources and targets having
333 raise CompileError ("Don't know how to compile %s" % src_name)
335 base = os.path.basename (base)
336 if ext in self._rc_extensions:
337 obj_names.append (os.path.join (output_dir,
338 base + self.res_extension))
339 elif ext in self._mc_extensions:
340 obj_names.append (os.path.join (output_dir,
341 base + self.res_extension))
343 obj_names.append (os.path.join (output_dir,
344 base + self.obj_extension))
347 # object_filenames ()
350 def compile(self, sources,
351 output_dir=None, macros=None, include_dirs=None, debug=0,
352 extra_preargs=None, extra_postargs=None, depends=None):
354 if not self.initialized: self.initialize()
355 macros, objects, extra_postargs, pp_opts, build = \
356 self._setup_compile(output_dir, macros, include_dirs, sources,
357 depends, extra_postargs)
359 compile_opts = extra_preargs or []
360 compile_opts.append ('/c')
362 compile_opts.extend(self.compile_options_debug)
364 compile_opts.extend(self.compile_options)
368 src, ext = build[obj]
372 # pass the full pathname to MSVC in debug mode,
373 # this allows the debugger to find the source file
374 # without asking the user to browse for it
375 src = os.path.abspath(src)
377 if ext in self._c_extensions:
378 input_opt = "/Tc" + src
379 elif ext in self._cpp_extensions:
380 input_opt = "/Tp" + src
381 elif ext in self._rc_extensions:
382 # compile .RC to .RES file
384 output_opt = "/fo" + obj
386 self.spawn ([self.rc] + pp_opts +
387 [output_opt] + [input_opt])
388 except DistutilsExecError, msg:
389 raise CompileError, msg
391 elif ext in self._mc_extensions:
393 # Compile .MC to .RC file to .RES file.
394 # * '-h dir' specifies the directory for the
395 # generated include file
396 # * '-r dir' specifies the target directory of the
397 # generated RC file and the binary message resource
400 # For now (since there are no options to change this),
401 # we use the source-directory for the include file and
402 # the build directory for the RC file and message
403 # resources. This works at least for win32all.
405 h_dir = os.path.dirname (src)
406 rc_dir = os.path.dirname (obj)
408 # first compile .MC to .RC and .H file
409 self.spawn ([self.mc] +
410 ['-h', h_dir, '-r', rc_dir] + [src])
411 base, _ = os.path.splitext (os.path.basename (src))
412 rc_file = os.path.join (rc_dir, base + '.rc')
413 # then compile .RC to .RES file
414 self.spawn ([self.rc] +
415 ["/fo" + obj] + [rc_file])
417 except DistutilsExecError, msg:
418 raise CompileError, msg
421 # how to handle this file?
423 "Don't know how to compile %s to %s" % \
426 output_opt = "/Fo" + obj
428 self.spawn ([self.cc] + compile_opts + pp_opts +
429 [input_opt, output_opt] +
431 except DistutilsExecError, msg:
432 raise CompileError, msg
439 def create_static_lib (self,
446 if not self.initialized: self.initialize()
447 (objects, output_dir) = self._fix_object_args (objects, output_dir)
449 self.library_filename (output_libname, output_dir=output_dir)
451 if self._need_link (objects, output_filename):
452 lib_args = objects + ['/OUT:' + output_filename]
454 pass # XXX what goes here?
456 self.spawn ([self.lib] + lib_args)
457 except DistutilsExecError, msg:
461 log.debug("skipping %s (up-to-date)", output_filename)
463 # create_static_lib ()
472 runtime_library_dirs=None,
480 if not self.initialized: self.initialize()
481 (objects, output_dir) = self._fix_object_args (objects, output_dir)
482 (libraries, library_dirs, runtime_library_dirs) = \
483 self._fix_lib_args (libraries, library_dirs, runtime_library_dirs)
485 if runtime_library_dirs:
486 self.warn ("I don't know what to do with 'runtime_library_dirs': "
487 + str (runtime_library_dirs))
489 lib_opts = gen_lib_options (self,
490 library_dirs, runtime_library_dirs,
492 if output_dir is not None:
493 output_filename = os.path.join (output_dir, output_filename)
495 if self._need_link (objects, output_filename):
497 if target_desc == CCompiler.EXECUTABLE:
499 ldflags = self.ldflags_shared_debug[1:]
501 ldflags = self.ldflags_shared[1:]
504 ldflags = self.ldflags_shared_debug
506 ldflags = self.ldflags_shared
509 for sym in (export_symbols or []):
510 export_opts.append("/EXPORT:" + sym)
512 ld_args = (ldflags + lib_opts + export_opts +
513 objects + ['/OUT:' + output_filename])
515 # The MSVC linker generates .lib and .exp files, which cannot be
516 # suppressed by any linker switches. The .lib files may even be
517 # needed! Make sure they are generated in the temporary build
518 # directory. Since they have different names for debug and release
519 # builds, they can go into the same directory.
520 if export_symbols is not None:
521 (dll_name, dll_ext) = os.path.splitext(
522 os.path.basename(output_filename))
523 implib_file = os.path.join(
524 os.path.dirname(objects[0]),
525 self.library_filename(dll_name))
526 ld_args.append ('/IMPLIB:' + implib_file)
529 ld_args[:0] = extra_preargs
531 ld_args.extend(extra_postargs)
533 self.mkpath (os.path.dirname (output_filename))
535 self.spawn ([self.linker] + ld_args)
536 except DistutilsExecError, msg:
540 log.debug("skipping %s (up-to-date)", output_filename)
545 # -- Miscellaneous methods -----------------------------------------
546 # These are all used by the 'gen_lib_options() function, in
549 def library_dir_option (self, dir):
550 return "/LIBPATH:" + dir
552 def runtime_library_dir_option (self, dir):
553 raise DistutilsPlatformError, \
554 "don't know how to set runtime library search path for MSVC++"
556 def library_option (self, lib):
557 return self.library_filename (lib)
560 def find_library_file (self, dirs, lib, debug=0):
561 # Prefer a debugging library if found (and requested), but deal
562 # with it if we don't have one.
564 try_names = [lib + "_d", lib]
568 for name in try_names:
569 libfile = os.path.join(dir, self.library_filename (name))
570 if os.path.exists(libfile):
573 # Oops, didn't find it in *any* of 'dirs'
576 # find_library_file ()
578 # Helper methods for using the MSVC registry settings
580 def find_exe(self, exe):
581 """Return path to an MSVC executable program.
583 Tries to find the program in several places: first, one of the
584 MSVC program search paths from the registry; next, the directories
585 in the PATH environment variable. If any of those work, return an
586 absolute path that is known to exist. If none of them work, just
587 return the original program name, 'exe'.
590 for p in self.__paths:
591 fn = os.path.join(os.path.abspath(p), exe)
592 if os.path.isfile(fn):
595 # didn't find it; try existing path
596 for p in string.split(os.environ['Path'],';'):
597 fn = os.path.join(os.path.abspath(p),exe)
598 if os.path.isfile(fn):
603 def get_msvc_paths(self, path, platform='x86'):
604 """Get a list of devstudio directories (include, lib or path).
606 Return a list of strings. The list will be empty if unable to
607 access the registry or appropriate registry keys not found.
610 if not _can_read_reg:
613 path = path + " dirs"
614 if self.__version >= 7:
615 key = (r"%s\%0.1f\VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories"
616 % (self.__root, self.__version))
618 key = (r"%s\6.0\Build System\Components\Platforms"
619 r"\Win32 (%s)\Directories" % (self.__root, platform))
622 d = read_values(base, key)
624 if self.__version >= 7:
625 return string.split(self.__macros.sub(d[path]), ";")
627 return string.split(d[path], ";")
628 # MSVC 6 seems to create the registry entries we need only when
630 if self.__version == 6:
632 if read_values(base, r"%s\6.0" % self.__root) is not None:
633 self.warn("It seems you have Visual Studio 6 installed, "
634 "but the expected registry settings are not present.\n"
635 "You must at least run the Visual Studio GUI once "
636 "so that these entries are created.")
640 def set_path_env_var(self, name):
641 """Set environment variable 'name' to an MSVC path type value.
643 This is equivalent to a SET command prior to execution of spawned
648 p = self.get_msvc_paths("library")
650 p = self.get_msvc_paths(name)
652 os.environ[name] = string.join(p, ';')