1 """distutils.msvc9compiler
3 Contains MSVCCompiler, an implementation of the abstract CCompiler class
4 for the Microsoft Visual Studio 2008.
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.
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
15 __revision__ = "$Id: msvc9compiler.py 68082 2008-12-30 23:06:46Z tarek.ziade $"
20 from distutils.errors import (DistutilsExecError, DistutilsPlatformError,
21 CompileError, LibError, LinkError)
22 from distutils.ccompiler import (CCompiler, gen_preprocess_options,
24 from distutils import log
25 from distutils.util import get_platform
29 RegOpenKeyEx = _winreg.OpenKeyEx
30 RegEnumKey = _winreg.EnumKey
31 RegEnumValue = _winreg.EnumValue
32 RegError = _winreg.error
34 HKEYS = (_winreg.HKEY_USERS,
35 _winreg.HKEY_CURRENT_USER,
36 _winreg.HKEY_LOCAL_MACHINE,
37 _winreg.HKEY_CLASSES_ROOT)
39 VS_BASE = r"Software\Microsoft\VisualStudio\%0.1f"
40 WINSDK_BASE = r"Software\Microsoft\Microsoft SDKs\Windows"
41 NET_BASE = r"Software\Microsoft\.NETFramework"
43 # A map keyed by get_platform() return values to values accepted by
44 # 'vcvarsall.bat'. Note a cross-compile may combine these (eg, 'x86_amd64' is
45 # the param to cross-compile on x86 targetting amd64.)
48 'win-amd64' : 'amd64',
53 """Helper class to read values from the registry
57 def get_value(cls, path, key):
59 d = cls.read_values(base, path)
65 def read_keys(cls, base, key):
66 """Return list of registry keys."""
68 handle = RegOpenKeyEx(base, key)
75 k = RegEnumKey(handle, i)
83 def read_values(cls, base, key):
84 """Return dict of registry keys and values.
86 All names are converted to lowercase.
89 handle = RegOpenKeyEx(base, key)
96 name, value, type = RegEnumValue(handle, i)
100 d[cls.convert_mbcs(name)] = cls.convert_mbcs(value)
106 dec = getattr(s, "decode", None)
116 def __init__(self, version):
118 self.vsbase = VS_BASE % version
119 self.load_macros(version)
121 def set_macro(self, macro, path, key):
122 self.macros["$(%s)" % macro] = Reg.get_value(path, key)
124 def load_macros(self, version):
125 self.set_macro("VCInstallDir", self.vsbase + r"\Setup\VC", "productdir")
126 self.set_macro("VSInstallDir", self.vsbase + r"\Setup\VS", "productdir")
127 self.set_macro("FrameworkDir", NET_BASE, "installroot")
130 self.set_macro("FrameworkSDKDir", NET_BASE,
131 "sdkinstallrootv2.0")
133 raise KeyError("sdkinstallrootv2.0")
134 except KeyError as exc: #
135 raise DistutilsPlatformError(
136 """Python was built with Visual Studio 2008;
137 extensions must be built with a compiler than can generate compatible binaries.
138 Visual Studio 2008 was not found on this system. If you have Cygwin installed,
139 you can try compiling with MingW32, by passing "-c mingw32" to setup.py.""")
142 self.set_macro("FrameworkVersion", self.vsbase, "clr version")
143 self.set_macro("WindowsSdkDir", WINSDK_BASE, "currentinstallfolder")
145 p = r"Software\Microsoft\NET Framework Setup\Product"
148 h = RegOpenKeyEx(base, p)
151 key = RegEnumKey(h, 0)
152 d = Reg.get_value(base, r"%s\%s" % (p, key))
153 self.macros["$(FrameworkVersion)"] = d["version"]
156 for k, v in self.macros.items():
160 def get_build_version():
161 """Return the version of MSVC that was used to build Python.
163 For Python 2.3 and up, the version number is included in
164 sys.version. For earlier versions, assume the compiler is MSVC 6.
167 i = sys.version.find(prefix)
171 s, rest = sys.version[i:].split(" ", 1)
172 majorVersion = int(s[:-2]) - 6
173 minorVersion = int(s[2:3]) / 10.0
174 # I don't think paths are affected by minor version in version 6
175 if majorVersion == 6:
177 if majorVersion >= 6:
178 return majorVersion + minorVersion
179 # else we don't know what version of the compiler this is
182 def normalize_and_reduce_paths(paths):
183 """Return a list of normalized paths with duplicates removed.
185 The current order of paths is maintained.
187 # Paths are normalized so things like: /a and /a/ aren't both preserved.
190 np = os.path.normpath(p)
191 # XXX(nnorwitz): O(n**2), if reduced_paths gets long perhaps use a set.
192 if np not in reduced_paths:
193 reduced_paths.append(np)
196 def removeDuplicates(variable):
197 """Remove duplicate values of an environment variable.
199 oldList = variable.split(os.pathsep)
204 newVariable = os.pathsep.join(newList)
207 def find_vcvarsall(version):
208 """Find the vcvarsall.bat file
210 At first it tries to find the productdir of VS 2008 in the registry. If
211 that fails it falls back to the VS90COMNTOOLS env var.
213 vsbase = VS_BASE % version
215 productdir = Reg.get_value(r"%s\Setup\VC" % vsbase,
218 log.debug("Unable to find productdir in registry")
221 if not productdir or not os.path.isdir(productdir):
222 toolskey = "VS%0.f0COMNTOOLS" % version
223 toolsdir = os.environ.get(toolskey, None)
225 if toolsdir and os.path.isdir(toolsdir):
226 productdir = os.path.join(toolsdir, os.pardir, os.pardir, "VC")
227 productdir = os.path.abspath(productdir)
228 if not os.path.isdir(productdir):
229 log.debug("%s is not a valid directory" % productdir)
232 log.debug("Env var %s is not set or invalid" % toolskey)
234 log.debug("No productdir found")
236 vcvarsall = os.path.join(productdir, "vcvarsall.bat")
237 if os.path.isfile(vcvarsall):
239 log.debug("Unable to find vcvarsall.bat")
242 def query_vcvarsall(version, arch="x86"):
243 """Launch vcvarsall.bat and read the settings from its environment
245 vcvarsall = find_vcvarsall(version)
246 interesting = set(("include", "lib", "libpath", "path"))
249 if vcvarsall is None:
250 raise DistutilsPlatformError("Unable to find vcvarsall.bat")
251 log.debug("Calling 'vcvarsall.bat %s' (version=%s)", arch, version)
252 popen = subprocess.Popen('"%s" %s & set' % (vcvarsall, arch),
253 stdout=subprocess.PIPE,
254 stderr=subprocess.PIPE)
256 stdout, stderr = popen.communicate()
257 if popen.wait() != 0:
258 raise DistutilsPlatformError(stderr.decode("mbcs"))
260 stdout = stdout.decode("mbcs")
261 for line in stdout.split("\n"):
262 line = Reg.convert_mbcs(line)
266 key, value = line.split('=', 1)
268 if key in interesting:
269 if value.endswith(os.pathsep):
271 result[key] = removeDuplicates(value)
273 if len(result) != len(interesting):
274 raise ValueError(str(list(result.keys())))
279 VERSION = get_build_version()
281 raise DistutilsPlatformError("VC %0.1f is not supported by this module" % VERSION)
282 # MACROS = MacroExpander(VERSION)
284 class MSVCCompiler(CCompiler) :
285 """Concrete class that implements an interface to Microsoft Visual C++,
286 as defined by the CCompiler abstract class."""
288 compiler_type = 'msvc'
290 # Just set this so CCompiler's constructor doesn't barf. We currently
291 # don't use the 'set_executables()' bureaucracy provided by CCompiler,
292 # as it really isn't necessary for this sort of single-compiler class.
293 # Would be nice to have a consistent interface with UnixCCompiler,
294 # though, so it's worth thinking about.
297 # Private class data (need to distinguish C from C++ source for compiler)
298 _c_extensions = ['.c']
299 _cpp_extensions = ['.cc', '.cpp', '.cxx']
300 _rc_extensions = ['.rc']
301 _mc_extensions = ['.mc']
303 # Needed for the filename generation methods provided by the
304 # base class, CCompiler.
305 src_extensions = (_c_extensions + _cpp_extensions +
306 _rc_extensions + _mc_extensions)
307 res_extension = '.res'
308 obj_extension = '.obj'
309 static_lib_extension = '.lib'
310 shared_lib_extension = '.dll'
311 static_lib_format = shared_lib_format = '%s%s'
312 exe_extension = '.exe'
314 def __init__(self, verbose=0, dry_run=0, force=0):
315 CCompiler.__init__ (self, verbose, dry_run, force)
316 self.__version = VERSION
317 self.__root = r"Software\Microsoft\VisualStudio"
318 # self.__macros = MACROS
320 # target platform (.plat_name is consistent with 'bdist')
321 self.plat_name = None
322 self.__arch = None # deprecated name
323 self.initialized = False
325 def initialize(self, plat_name=None):
326 # multi-init means we would need to check platform same each time...
327 assert not self.initialized, "don't init multiple times"
328 if plat_name is None:
329 plat_name = get_platform()
330 # sanity check for platforms to prevent obscure errors later.
331 ok_plats = 'win32', 'win-amd64', 'win-ia64'
332 if plat_name not in ok_plats:
333 raise DistutilsPlatformError("--plat-name must be one of %s" %
336 if "DISTUTILS_USE_SDK" in os.environ and "MSSdk" in os.environ and self.find_exe("cl.exe"):
337 # Assume that the SDK set up everything alright; don't try to be
340 self.linker = "link.exe"
345 # On x86, 'vcvars32.bat amd64' creates an env that doesn't work;
346 # to cross compile, you use 'x86_amd64'.
347 # On AMD64, 'vcvars32.bat amd64' is a native build env; to cross
348 # compile use 'x86' (ie, it runs the x86 compiler directly)
349 # No idea how itanium handles this, if at all.
350 if plat_name == get_platform() or plat_name == 'win32':
351 # native build or cross-compile to win32
352 plat_spec = PLAT_TO_VCVARS[plat_name]
354 # cross compile from win32 -> some 64bit
355 plat_spec = PLAT_TO_VCVARS[get_platform()] + '_' + \
356 PLAT_TO_VCVARS[plat_name]
358 vc_env = query_vcvarsall(VERSION, plat_spec)
360 # take care to only use strings in the environment.
361 self.__paths = vc_env['path'].encode('mbcs').split(os.pathsep)
362 os.environ['lib'] = vc_env['lib'].encode('mbcs')
363 os.environ['include'] = vc_env['include'].encode('mbcs')
365 if len(self.__paths) == 0:
366 raise DistutilsPlatformError("Python was built with %s, "
367 "and extensions need to be built with the same "
368 "version of the compiler, but it isn't installed."
371 self.cc = self.find_exe("cl.exe")
372 self.linker = self.find_exe("link.exe")
373 self.lib = self.find_exe("lib.exe")
374 self.rc = self.find_exe("rc.exe") # resource compiler
375 self.mc = self.find_exe("mc.exe") # message compiler
376 #self.set_path_env_var('lib')
377 #self.set_path_env_var('include')
379 # extend the MSVC path with the current path
381 for p in os.environ['path'].split(';'):
382 self.__paths.append(p)
385 self.__paths = normalize_and_reduce_paths(self.__paths)
386 os.environ['path'] = ";".join(self.__paths)
388 self.preprocess_options = None
389 if self.__arch == "x86":
390 self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3',
392 self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3',
396 self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', '/GS-' ,
398 self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/GS-',
401 self.ldflags_shared = ['/DLL', '/nologo', '/INCREMENTAL:NO']
402 if self.__version >= 7:
403 self.ldflags_shared_debug = [
404 '/DLL', '/nologo', '/INCREMENTAL:no', '/DEBUG', '/pdb:None'
406 self.ldflags_static = [ '/nologo']
408 self.initialized = True
410 # -- Worker methods ------------------------------------------------
412 def object_filenames(self,
416 # Copied from ccompiler.py, extended to return .res as 'object'-file
418 if output_dir is None: output_dir = ''
420 for src_name in source_filenames:
421 (base, ext) = os.path.splitext (src_name)
422 base = os.path.splitdrive(base)[1] # Chop off the drive
423 base = base[os.path.isabs(base):] # If abs, chop off leading /
424 if ext not in self.src_extensions:
425 # Better to raise an exception instead of silently continuing
426 # and later complain about sources and targets having
428 raise CompileError ("Don't know how to compile %s" % src_name)
430 base = os.path.basename (base)
431 if ext in self._rc_extensions:
432 obj_names.append (os.path.join (output_dir,
433 base + self.res_extension))
434 elif ext in self._mc_extensions:
435 obj_names.append (os.path.join (output_dir,
436 base + self.res_extension))
438 obj_names.append (os.path.join (output_dir,
439 base + self.obj_extension))
443 def compile(self, sources,
444 output_dir=None, macros=None, include_dirs=None, debug=0,
445 extra_preargs=None, extra_postargs=None, depends=None):
447 if not self.initialized:
449 compile_info = self._setup_compile(output_dir, macros, include_dirs,
450 sources, depends, extra_postargs)
451 macros, objects, extra_postargs, pp_opts, build = compile_info
453 compile_opts = extra_preargs or []
454 compile_opts.append ('/c')
456 compile_opts.extend(self.compile_options_debug)
458 compile_opts.extend(self.compile_options)
462 src, ext = build[obj]
466 # pass the full pathname to MSVC in debug mode,
467 # this allows the debugger to find the source file
468 # without asking the user to browse for it
469 src = os.path.abspath(src)
471 if ext in self._c_extensions:
472 input_opt = "/Tc" + src
473 elif ext in self._cpp_extensions:
474 input_opt = "/Tp" + src
475 elif ext in self._rc_extensions:
476 # compile .RC to .RES file
478 output_opt = "/fo" + obj
480 self.spawn([self.rc] + pp_opts +
481 [output_opt] + [input_opt])
482 except DistutilsExecError as msg:
483 raise CompileError(msg)
485 elif ext in self._mc_extensions:
486 # Compile .MC to .RC file to .RES file.
487 # * '-h dir' specifies the directory for the
488 # generated include file
489 # * '-r dir' specifies the target directory of the
490 # generated RC file and the binary message resource
493 # For now (since there are no options to change this),
494 # we use the source-directory for the include file and
495 # the build directory for the RC file and message
496 # resources. This works at least for win32all.
497 h_dir = os.path.dirname(src)
498 rc_dir = os.path.dirname(obj)
500 # first compile .MC to .RC and .H file
501 self.spawn([self.mc] +
502 ['-h', h_dir, '-r', rc_dir] + [src])
503 base, _ = os.path.splitext (os.path.basename (src))
504 rc_file = os.path.join (rc_dir, base + '.rc')
505 # then compile .RC to .RES file
506 self.spawn([self.rc] +
507 ["/fo" + obj] + [rc_file])
509 except DistutilsExecError as msg:
510 raise CompileError(msg)
513 # how to handle this file?
514 raise CompileError("Don't know how to compile %s to %s"
517 output_opt = "/Fo" + obj
519 self.spawn([self.cc] + compile_opts + pp_opts +
520 [input_opt, output_opt] +
522 except DistutilsExecError as msg:
523 raise CompileError(msg)
528 def create_static_lib(self,
535 if not self.initialized:
537 (objects, output_dir) = self._fix_object_args(objects, output_dir)
538 output_filename = self.library_filename(output_libname,
539 output_dir=output_dir)
541 if self._need_link(objects, output_filename):
542 lib_args = objects + ['/OUT:' + output_filename]
544 pass # XXX what goes here?
546 self.spawn([self.lib] + lib_args)
547 except DistutilsExecError as msg:
550 log.debug("skipping %s (up-to-date)", output_filename)
560 runtime_library_dirs=None,
568 if not self.initialized:
570 (objects, output_dir) = self._fix_object_args(objects, output_dir)
571 fixed_args = self._fix_lib_args(libraries, library_dirs,
572 runtime_library_dirs)
573 (libraries, library_dirs, runtime_library_dirs) = fixed_args
575 if runtime_library_dirs:
576 self.warn ("I don't know what to do with 'runtime_library_dirs': "
577 + str (runtime_library_dirs))
579 lib_opts = gen_lib_options(self,
580 library_dirs, runtime_library_dirs,
582 if output_dir is not None:
583 output_filename = os.path.join(output_dir, output_filename)
585 if self._need_link(objects, output_filename):
586 if target_desc == CCompiler.EXECUTABLE:
588 ldflags = self.ldflags_shared_debug[1:]
590 ldflags = self.ldflags_shared[1:]
593 ldflags = self.ldflags_shared_debug
595 ldflags = self.ldflags_shared
598 for sym in (export_symbols or []):
599 export_opts.append("/EXPORT:" + sym)
601 ld_args = (ldflags + lib_opts + export_opts +
602 objects + ['/OUT:' + output_filename])
604 # The MSVC linker generates .lib and .exp files, which cannot be
605 # suppressed by any linker switches. The .lib files may even be
606 # needed! Make sure they are generated in the temporary build
607 # directory. Since they have different names for debug and release
608 # builds, they can go into the same directory.
609 build_temp = os.path.dirname(objects[0])
610 if export_symbols is not None:
611 (dll_name, dll_ext) = os.path.splitext(
612 os.path.basename(output_filename))
613 implib_file = os.path.join(
615 self.library_filename(dll_name))
616 ld_args.append ('/IMPLIB:' + implib_file)
618 # Embedded manifests are recommended - see MSDN article titled
619 # "How to: Embed a Manifest Inside a C/C++ Application"
620 # (currently at http://msdn2.microsoft.com/en-us/library/ms235591(VS.80).aspx)
621 # Ask the linker to generate the manifest in the temp dir, so
622 # we can embed it later.
623 temp_manifest = os.path.join(
625 os.path.basename(output_filename) + ".manifest")
626 ld_args.append('/MANIFESTFILE:' + temp_manifest)
629 ld_args[:0] = extra_preargs
631 ld_args.extend(extra_postargs)
633 self.mkpath(os.path.dirname(output_filename))
635 self.spawn([self.linker] + ld_args)
636 except DistutilsExecError as msg:
640 # XXX - this is somewhat fragile - if mt.exe fails, distutils
641 # will still consider the DLL up-to-date, but it will not have a
642 # manifest. Maybe we should link to a temp file? OTOH, that
643 # implies a build environment error that shouldn't go undetected.
644 mfid = 1 if target_desc == CCompiler.EXECUTABLE else 2
645 out_arg = '-outputresource:%s;%s' % (output_filename, mfid)
647 self.spawn(['mt.exe', '-nologo', '-manifest',
648 temp_manifest, out_arg])
649 except DistutilsExecError as msg:
652 log.debug("skipping %s (up-to-date)", output_filename)
655 # -- Miscellaneous methods -----------------------------------------
656 # These are all used by the 'gen_lib_options() function, in
659 def library_dir_option(self, dir):
660 return "/LIBPATH:" + dir
662 def runtime_library_dir_option(self, dir):
663 raise DistutilsPlatformError(
664 "don't know how to set runtime library search path for MSVC++")
666 def library_option(self, lib):
667 return self.library_filename(lib)
670 def find_library_file(self, dirs, lib, debug=0):
671 # Prefer a debugging library if found (and requested), but deal
672 # with it if we don't have one.
674 try_names = [lib + "_d", lib]
678 for name in try_names:
679 libfile = os.path.join(dir, self.library_filename (name))
680 if os.path.exists(libfile):
683 # Oops, didn't find it in *any* of 'dirs'
686 # Helper methods for using the MSVC registry settings
688 def find_exe(self, exe):
689 """Return path to an MSVC executable program.
691 Tries to find the program in several places: first, one of the
692 MSVC program search paths from the registry; next, the directories
693 in the PATH environment variable. If any of those work, return an
694 absolute path that is known to exist. If none of them work, just
695 return the original program name, 'exe'.
697 for p in self.__paths:
698 fn = os.path.join(os.path.abspath(p), exe)
699 if os.path.isfile(fn):
702 # didn't find it; try existing path
703 for p in os.environ['Path'].split(';'):
704 fn = os.path.join(os.path.abspath(p),exe)
705 if os.path.isfile(fn):