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
22 from distutils
.errors
import (DistutilsExecError
, DistutilsPlatformError
,
23 CompileError
, LibError
, LinkError
)
24 from distutils
.ccompiler
import CCompiler
, gen_lib_options
25 from distutils
import log
26 from distutils
.util
import get_platform
30 RegOpenKeyEx
= _winreg
.OpenKeyEx
31 RegEnumKey
= _winreg
.EnumKey
32 RegEnumValue
= _winreg
.EnumValue
33 RegError
= _winreg
.error
35 HKEYS
= (_winreg
.HKEY_USERS
,
36 _winreg
.HKEY_CURRENT_USER
,
37 _winreg
.HKEY_LOCAL_MACHINE
,
38 _winreg
.HKEY_CLASSES_ROOT
)
40 NATIVE_WIN64
= (sys
.platform
== 'win32' and sys
.maxsize
> 2**32)
42 # Visual C++ is a 32-bit application, so we need to look in
43 # the corresponding registry branch, if we're running a
44 # 64-bit Python on Win64
45 VS_BASE
= r
"Software\Wow6432Node\Microsoft\VisualStudio\%0.1f"
46 VSEXPRESS_BASE
= r
"Software\Wow6432Node\Microsoft\VCExpress\%0.1f"
47 WINSDK_BASE
= r
"Software\Wow6432Node\Microsoft\Microsoft SDKs\Windows"
48 NET_BASE
= r
"Software\Wow6432Node\Microsoft\.NETFramework"
50 VS_BASE
= r
"Software\Microsoft\VisualStudio\%0.1f"
51 VSEXPRESS_BASE
= r
"Software\Microsoft\VCExpress\%0.1f"
52 WINSDK_BASE
= r
"Software\Microsoft\Microsoft SDKs\Windows"
53 NET_BASE
= r
"Software\Microsoft\.NETFramework"
55 # A map keyed by get_platform() return values to values accepted by
56 # 'vcvarsall.bat'. Note a cross-compile may combine these (eg, 'x86_amd64' is
57 # the param to cross-compile on x86 targetting amd64.)
60 'win-amd64' : 'amd64',
65 """Helper class to read values from the registry
68 def get_value(cls
, path
, key
):
70 d
= cls
.read_values(base
, path
)
74 get_value
= classmethod(get_value
)
76 def read_keys(cls
, base
, key
):
77 """Return list of registry keys."""
79 handle
= RegOpenKeyEx(base
, key
)
86 k
= RegEnumKey(handle
, i
)
92 read_keys
= classmethod(read_keys
)
94 def read_values(cls
, base
, key
):
95 """Return dict of registry keys and values.
97 All names are converted to lowercase.
100 handle
= RegOpenKeyEx(base
, key
)
107 name
, value
, type = RegEnumValue(handle
, i
)
111 d
[cls
.convert_mbcs(name
)] = cls
.convert_mbcs(value
)
114 read_values
= classmethod(read_values
)
117 dec
= getattr(s
, "decode", None)
124 convert_mbcs
= staticmethod(convert_mbcs
)
128 def __init__(self
, version
):
130 self
.vsbase
= VS_BASE
% version
131 self
.load_macros(version
)
133 def set_macro(self
, macro
, path
, key
):
134 self
.macros
["$(%s)" % macro
] = Reg
.get_value(path
, key
)
136 def load_macros(self
, version
):
137 self
.set_macro("VCInstallDir", self
.vsbase
+ r
"\Setup\VC", "productdir")
138 self
.set_macro("VSInstallDir", self
.vsbase
+ r
"\Setup\VS", "productdir")
139 self
.set_macro("FrameworkDir", NET_BASE
, "installroot")
142 self
.set_macro("FrameworkSDKDir", NET_BASE
,
143 "sdkinstallrootv2.0")
145 raise KeyError("sdkinstallrootv2.0")
147 raise DistutilsPlatformError(
148 """Python was built with Visual Studio 2008;
149 extensions must be built with a compiler than can generate compatible binaries.
150 Visual Studio 2008 was not found on this system. If you have Cygwin installed,
151 you can try compiling with MingW32, by passing "-c mingw32" to setup.py.""")
154 self
.set_macro("FrameworkVersion", self
.vsbase
, "clr version")
155 self
.set_macro("WindowsSdkDir", WINSDK_BASE
, "currentinstallfolder")
157 p
= r
"Software\Microsoft\NET Framework Setup\Product"
160 h
= RegOpenKeyEx(base
, p
)
163 key
= RegEnumKey(h
, 0)
164 d
= Reg
.get_value(base
, r
"%s\%s" % (p
, key
))
165 self
.macros
["$(FrameworkVersion)"] = d
["version"]
168 for k
, v
in self
.macros
.items():
172 def get_build_version():
173 """Return the version of MSVC that was used to build Python.
175 For Python 2.3 and up, the version number is included in
176 sys.version. For earlier versions, assume the compiler is MSVC 6.
179 i
= sys
.version
.find(prefix
)
183 s
, rest
= sys
.version
[i
:].split(" ", 1)
184 majorVersion
= int(s
[:-2]) - 6
185 minorVersion
= int(s
[2:3]) / 10.0
186 # I don't think paths are affected by minor version in version 6
187 if majorVersion
== 6:
189 if majorVersion
>= 6:
190 return majorVersion
+ minorVersion
191 # else we don't know what version of the compiler this is
194 def normalize_and_reduce_paths(paths
):
195 """Return a list of normalized paths with duplicates removed.
197 The current order of paths is maintained.
199 # Paths are normalized so things like: /a and /a/ aren't both preserved.
202 np
= os
.path
.normpath(p
)
203 # XXX(nnorwitz): O(n**2), if reduced_paths gets long perhaps use a set.
204 if np
not in reduced_paths
:
205 reduced_paths
.append(np
)
208 def removeDuplicates(variable
):
209 """Remove duplicate values of an environment variable.
211 oldList
= variable
.split(os
.pathsep
)
216 newVariable
= os
.pathsep
.join(newList
)
219 def find_vcvarsall(version
):
220 """Find the vcvarsall.bat file
222 At first it tries to find the productdir of VS 2008 in the registry. If
223 that fails it falls back to the VS90COMNTOOLS env var.
225 vsbase
= VS_BASE
% version
227 productdir
= Reg
.get_value(r
"%s\Setup\VC" % vsbase
,
232 # trying Express edition
233 if productdir
is None:
234 vsbase
= VSEXPRESS_BASE
% version
236 productdir
= Reg
.get_value(r
"%s\Setup\VC" % vsbase
,
240 log
.debug("Unable to find productdir in registry")
242 if not productdir
or not os
.path
.isdir(productdir
):
243 toolskey
= "VS%0.f0COMNTOOLS" % version
244 toolsdir
= os
.environ
.get(toolskey
, None)
246 if toolsdir
and os
.path
.isdir(toolsdir
):
247 productdir
= os
.path
.join(toolsdir
, os
.pardir
, os
.pardir
, "VC")
248 productdir
= os
.path
.abspath(productdir
)
249 if not os
.path
.isdir(productdir
):
250 log
.debug("%s is not a valid directory" % productdir
)
253 log
.debug("Env var %s is not set or invalid" % toolskey
)
255 log
.debug("No productdir found")
257 vcvarsall
= os
.path
.join(productdir
, "vcvarsall.bat")
258 if os
.path
.isfile(vcvarsall
):
260 log
.debug("Unable to find vcvarsall.bat")
263 def query_vcvarsall(version
, arch
="x86"):
264 """Launch vcvarsall.bat and read the settings from its environment
266 vcvarsall
= find_vcvarsall(version
)
267 interesting
= set(("include", "lib", "libpath", "path"))
270 if vcvarsall
is None:
271 raise DistutilsPlatformError("Unable to find vcvarsall.bat")
272 log
.debug("Calling 'vcvarsall.bat %s' (version=%s)", arch
, version
)
273 popen
= subprocess
.Popen('"%s" %s & set' % (vcvarsall
, arch
),
274 stdout
=subprocess
.PIPE
,
275 stderr
=subprocess
.PIPE
)
277 stdout
, stderr
= popen
.communicate()
278 if popen
.wait() != 0:
279 raise DistutilsPlatformError(stderr
.decode("mbcs"))
281 stdout
= stdout
.decode("mbcs")
282 for line
in stdout
.split("\n"):
283 line
= Reg
.convert_mbcs(line
)
287 key
, value
= line
.split('=', 1)
289 if key
in interesting
:
290 if value
.endswith(os
.pathsep
):
292 result
[key
] = removeDuplicates(value
)
294 if len(result
) != len(interesting
):
295 raise ValueError(str(list(result
.keys())))
300 VERSION
= get_build_version()
302 raise DistutilsPlatformError("VC %0.1f is not supported by this module" % VERSION
)
303 # MACROS = MacroExpander(VERSION)
305 class MSVCCompiler(CCompiler
) :
306 """Concrete class that implements an interface to Microsoft Visual C++,
307 as defined by the CCompiler abstract class."""
309 compiler_type
= 'msvc'
311 # Just set this so CCompiler's constructor doesn't barf. We currently
312 # don't use the 'set_executables()' bureaucracy provided by CCompiler,
313 # as it really isn't necessary for this sort of single-compiler class.
314 # Would be nice to have a consistent interface with UnixCCompiler,
315 # though, so it's worth thinking about.
318 # Private class data (need to distinguish C from C++ source for compiler)
319 _c_extensions
= ['.c']
320 _cpp_extensions
= ['.cc', '.cpp', '.cxx']
321 _rc_extensions
= ['.rc']
322 _mc_extensions
= ['.mc']
324 # Needed for the filename generation methods provided by the
325 # base class, CCompiler.
326 src_extensions
= (_c_extensions
+ _cpp_extensions
+
327 _rc_extensions
+ _mc_extensions
)
328 res_extension
= '.res'
329 obj_extension
= '.obj'
330 static_lib_extension
= '.lib'
331 shared_lib_extension
= '.dll'
332 static_lib_format
= shared_lib_format
= '%s%s'
333 exe_extension
= '.exe'
335 def __init__(self
, verbose
=0, dry_run
=0, force
=0):
336 CCompiler
.__init
__ (self
, verbose
, dry_run
, force
)
337 self
.__version
= VERSION
338 self
.__root
= r
"Software\Microsoft\VisualStudio"
339 # self.__macros = MACROS
341 # target platform (.plat_name is consistent with 'bdist')
342 self
.plat_name
= None
343 self
.__arch
= None # deprecated name
344 self
.initialized
= False
346 def initialize(self
, plat_name
=None):
347 # multi-init means we would need to check platform same each time...
348 assert not self
.initialized
, "don't init multiple times"
349 if plat_name
is None:
350 plat_name
= get_platform()
351 # sanity check for platforms to prevent obscure errors later.
352 ok_plats
= 'win32', 'win-amd64', 'win-ia64'
353 if plat_name
not in ok_plats
:
354 raise DistutilsPlatformError("--plat-name must be one of %s" %
357 if "DISTUTILS_USE_SDK" in os
.environ
and "MSSdk" in os
.environ
and self
.find_exe("cl.exe"):
358 # Assume that the SDK set up everything alright; don't try to be
361 self
.linker
= "link.exe"
366 # On x86, 'vcvars32.bat amd64' creates an env that doesn't work;
367 # to cross compile, you use 'x86_amd64'.
368 # On AMD64, 'vcvars32.bat amd64' is a native build env; to cross
369 # compile use 'x86' (ie, it runs the x86 compiler directly)
370 # No idea how itanium handles this, if at all.
371 if plat_name
== get_platform() or plat_name
== 'win32':
372 # native build or cross-compile to win32
373 plat_spec
= PLAT_TO_VCVARS
[plat_name
]
375 # cross compile from win32 -> some 64bit
376 plat_spec
= PLAT_TO_VCVARS
[get_platform()] + '_' + \
377 PLAT_TO_VCVARS
[plat_name
]
379 vc_env
= query_vcvarsall(VERSION
, plat_spec
)
381 # take care to only use strings in the environment.
382 self
.__paths
= vc_env
['path'].encode('mbcs').split(os
.pathsep
)
383 os
.environ
['lib'] = vc_env
['lib'].encode('mbcs')
384 os
.environ
['include'] = vc_env
['include'].encode('mbcs')
386 if len(self
.__paths
) == 0:
387 raise DistutilsPlatformError("Python was built with %s, "
388 "and extensions need to be built with the same "
389 "version of the compiler, but it isn't installed."
392 self
.cc
= self
.find_exe("cl.exe")
393 self
.linker
= self
.find_exe("link.exe")
394 self
.lib
= self
.find_exe("lib.exe")
395 self
.rc
= self
.find_exe("rc.exe") # resource compiler
396 self
.mc
= self
.find_exe("mc.exe") # message compiler
397 #self.set_path_env_var('lib')
398 #self.set_path_env_var('include')
400 # extend the MSVC path with the current path
402 for p
in os
.environ
['path'].split(';'):
403 self
.__paths
.append(p
)
406 self
.__paths
= normalize_and_reduce_paths(self
.__paths
)
407 os
.environ
['path'] = ";".join(self
.__paths
)
409 self
.preprocess_options
= None
410 if self
.__arch
== "x86":
411 self
.compile_options
= [ '/nologo', '/Ox', '/MD', '/W3',
413 self
.compile_options_debug
= ['/nologo', '/Od', '/MDd', '/W3',
417 self
.compile_options
= [ '/nologo', '/Ox', '/MD', '/W3', '/GS-' ,
419 self
.compile_options_debug
= ['/nologo', '/Od', '/MDd', '/W3', '/GS-',
422 self
.ldflags_shared
= ['/DLL', '/nologo', '/INCREMENTAL:NO']
423 if self
.__version
>= 7:
424 self
.ldflags_shared_debug
= [
425 '/DLL', '/nologo', '/INCREMENTAL:no', '/DEBUG', '/pdb:None'
427 self
.ldflags_static
= [ '/nologo']
429 self
.initialized
= True
431 # -- Worker methods ------------------------------------------------
433 def object_filenames(self
,
437 # Copied from ccompiler.py, extended to return .res as 'object'-file
439 if output_dir
is None: output_dir
= ''
441 for src_name
in source_filenames
:
442 (base
, ext
) = os
.path
.splitext (src_name
)
443 base
= os
.path
.splitdrive(base
)[1] # Chop off the drive
444 base
= base
[os
.path
.isabs(base
):] # If abs, chop off leading /
445 if ext
not in self
.src_extensions
:
446 # Better to raise an exception instead of silently continuing
447 # and later complain about sources and targets having
449 raise CompileError ("Don't know how to compile %s" % src_name
)
451 base
= os
.path
.basename (base
)
452 if ext
in self
._rc
_extensions
:
453 obj_names
.append (os
.path
.join (output_dir
,
454 base
+ self
.res_extension
))
455 elif ext
in self
._mc
_extensions
:
456 obj_names
.append (os
.path
.join (output_dir
,
457 base
+ self
.res_extension
))
459 obj_names
.append (os
.path
.join (output_dir
,
460 base
+ self
.obj_extension
))
464 def compile(self
, sources
,
465 output_dir
=None, macros
=None, include_dirs
=None, debug
=0,
466 extra_preargs
=None, extra_postargs
=None, depends
=None):
468 if not self
.initialized
:
470 compile_info
= self
._setup
_compile
(output_dir
, macros
, include_dirs
,
471 sources
, depends
, extra_postargs
)
472 macros
, objects
, extra_postargs
, pp_opts
, build
= compile_info
474 compile_opts
= extra_preargs
or []
475 compile_opts
.append ('/c')
477 compile_opts
.extend(self
.compile_options_debug
)
479 compile_opts
.extend(self
.compile_options
)
483 src
, ext
= build
[obj
]
487 # pass the full pathname to MSVC in debug mode,
488 # this allows the debugger to find the source file
489 # without asking the user to browse for it
490 src
= os
.path
.abspath(src
)
492 if ext
in self
._c
_extensions
:
493 input_opt
= "/Tc" + src
494 elif ext
in self
._cpp
_extensions
:
495 input_opt
= "/Tp" + src
496 elif ext
in self
._rc
_extensions
:
497 # compile .RC to .RES file
499 output_opt
= "/fo" + obj
501 self
.spawn([self
.rc
] + pp_opts
+
502 [output_opt
] + [input_opt
])
503 except DistutilsExecError
, msg
:
504 raise CompileError(msg
)
506 elif ext
in self
._mc
_extensions
:
507 # Compile .MC to .RC file to .RES file.
508 # * '-h dir' specifies the directory for the
509 # generated include file
510 # * '-r dir' specifies the target directory of the
511 # generated RC file and the binary message resource
514 # For now (since there are no options to change this),
515 # we use the source-directory for the include file and
516 # the build directory for the RC file and message
517 # resources. This works at least for win32all.
518 h_dir
= os
.path
.dirname(src
)
519 rc_dir
= os
.path
.dirname(obj
)
521 # first compile .MC to .RC and .H file
522 self
.spawn([self
.mc
] +
523 ['-h', h_dir
, '-r', rc_dir
] + [src
])
524 base
, _
= os
.path
.splitext (os
.path
.basename (src
))
525 rc_file
= os
.path
.join (rc_dir
, base
+ '.rc')
526 # then compile .RC to .RES file
527 self
.spawn([self
.rc
] +
528 ["/fo" + obj
] + [rc_file
])
530 except DistutilsExecError
, msg
:
531 raise CompileError(msg
)
534 # how to handle this file?
535 raise CompileError("Don't know how to compile %s to %s"
538 output_opt
= "/Fo" + obj
540 self
.spawn([self
.cc
] + compile_opts
+ pp_opts
+
541 [input_opt
, output_opt
] +
543 except DistutilsExecError
, msg
:
544 raise CompileError(msg
)
549 def create_static_lib(self
,
556 if not self
.initialized
:
558 (objects
, output_dir
) = self
._fix
_object
_args
(objects
, output_dir
)
559 output_filename
= self
.library_filename(output_libname
,
560 output_dir
=output_dir
)
562 if self
._need
_link
(objects
, output_filename
):
563 lib_args
= objects
+ ['/OUT:' + output_filename
]
565 pass # XXX what goes here?
567 self
.spawn([self
.lib
] + lib_args
)
568 except DistutilsExecError
, msg
:
571 log
.debug("skipping %s (up-to-date)", output_filename
)
581 runtime_library_dirs
=None,
589 if not self
.initialized
:
591 (objects
, output_dir
) = self
._fix
_object
_args
(objects
, output_dir
)
592 fixed_args
= self
._fix
_lib
_args
(libraries
, library_dirs
,
593 runtime_library_dirs
)
594 (libraries
, library_dirs
, runtime_library_dirs
) = fixed_args
596 if runtime_library_dirs
:
597 self
.warn ("I don't know what to do with 'runtime_library_dirs': "
598 + str (runtime_library_dirs
))
600 lib_opts
= gen_lib_options(self
,
601 library_dirs
, runtime_library_dirs
,
603 if output_dir
is not None:
604 output_filename
= os
.path
.join(output_dir
, output_filename
)
606 if self
._need
_link
(objects
, output_filename
):
607 if target_desc
== CCompiler
.EXECUTABLE
:
609 ldflags
= self
.ldflags_shared_debug
[1:]
611 ldflags
= self
.ldflags_shared
[1:]
614 ldflags
= self
.ldflags_shared_debug
616 ldflags
= self
.ldflags_shared
619 for sym
in (export_symbols
or []):
620 export_opts
.append("/EXPORT:" + sym
)
622 ld_args
= (ldflags
+ lib_opts
+ export_opts
+
623 objects
+ ['/OUT:' + output_filename
])
625 # The MSVC linker generates .lib and .exp files, which cannot be
626 # suppressed by any linker switches. The .lib files may even be
627 # needed! Make sure they are generated in the temporary build
628 # directory. Since they have different names for debug and release
629 # builds, they can go into the same directory.
630 build_temp
= os
.path
.dirname(objects
[0])
631 if export_symbols
is not None:
632 (dll_name
, dll_ext
) = os
.path
.splitext(
633 os
.path
.basename(output_filename
))
634 implib_file
= os
.path
.join(
636 self
.library_filename(dll_name
))
637 ld_args
.append ('/IMPLIB:' + implib_file
)
639 # Embedded manifests are recommended - see MSDN article titled
640 # "How to: Embed a Manifest Inside a C/C++ Application"
641 # (currently at http://msdn2.microsoft.com/en-us/library/ms235591(VS.80).aspx)
642 # Ask the linker to generate the manifest in the temp dir, so
643 # we can embed it later.
644 temp_manifest
= os
.path
.join(
646 os
.path
.basename(output_filename
) + ".manifest")
647 ld_args
.append('/MANIFESTFILE:' + temp_manifest
)
650 ld_args
[:0] = extra_preargs
652 ld_args
.extend(extra_postargs
)
654 self
.mkpath(os
.path
.dirname(output_filename
))
656 self
.spawn([self
.linker
] + ld_args
)
657 except DistutilsExecError
, msg
:
661 # XXX - this is somewhat fragile - if mt.exe fails, distutils
662 # will still consider the DLL up-to-date, but it will not have a
663 # manifest. Maybe we should link to a temp file? OTOH, that
664 # implies a build environment error that shouldn't go undetected.
665 if target_desc
== CCompiler
.EXECUTABLE
:
669 self
._remove
_visual
_c
_ref
(temp_manifest
)
670 out_arg
= '-outputresource:%s;%s' % (output_filename
, mfid
)
672 self
.spawn(['mt.exe', '-nologo', '-manifest',
673 temp_manifest
, out_arg
])
674 except DistutilsExecError
, msg
:
677 log
.debug("skipping %s (up-to-date)", output_filename
)
679 def _remove_visual_c_ref(self
, manifest_file
):
681 # Remove references to the Visual C runtime, so they will
682 # fall through to the Visual C dependency of Python.exe.
683 # This way, when installed for a restricted user (e.g.
684 # runtimes are not in WinSxS folder, but in Python's own
685 # folder), the runtimes do not need to be in every folder
687 manifest_f
= open(manifest_file
)
689 manifest_buf
= manifest_f
.read()
692 pattern
= re
.compile(
693 r
"""<assemblyIdentity.*?name=("|')Microsoft\."""\
694 r
"""VC\d{2}\.CRT("|').*?(/>|</assemblyIdentity>)""",
696 manifest_buf
= re
.sub(pattern
, "", manifest_buf
)
697 pattern
= "<dependentAssembly>\s*</dependentAssembly>"
698 manifest_buf
= re
.sub(pattern
, "", manifest_buf
)
699 manifest_f
= open(manifest_file
, 'w')
701 manifest_f
.write(manifest_buf
)
707 # -- Miscellaneous methods -----------------------------------------
708 # These are all used by the 'gen_lib_options() function, in
711 def library_dir_option(self
, dir):
712 return "/LIBPATH:" + dir
714 def runtime_library_dir_option(self
, dir):
715 raise DistutilsPlatformError(
716 "don't know how to set runtime library search path for MSVC++")
718 def library_option(self
, lib
):
719 return self
.library_filename(lib
)
722 def find_library_file(self
, dirs
, lib
, debug
=0):
723 # Prefer a debugging library if found (and requested), but deal
724 # with it if we don't have one.
726 try_names
= [lib
+ "_d", lib
]
730 for name
in try_names
:
731 libfile
= os
.path
.join(dir, self
.library_filename (name
))
732 if os
.path
.exists(libfile
):
735 # Oops, didn't find it in *any* of 'dirs'
738 # Helper methods for using the MSVC registry settings
740 def find_exe(self
, exe
):
741 """Return path to an MSVC executable program.
743 Tries to find the program in several places: first, one of the
744 MSVC program search paths from the registry; next, the directories
745 in the PATH environment variable. If any of those work, return an
746 absolute path that is known to exist. If none of them work, just
747 return the original program name, 'exe'.
749 for p
in self
.__paths
:
750 fn
= os
.path
.join(os
.path
.abspath(p
), exe
)
751 if os
.path
.isfile(fn
):
754 # didn't find it; try existing path
755 for p
in os
.environ
['Path'].split(';'):
756 fn
= os
.path
.join(os
.path
.abspath(p
),exe
)
757 if os
.path
.isfile(fn
):