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
21 from distutils
.errors
import DistutilsExecError
, DistutilsPlatformError
, \
22 CompileError
, LibError
, LinkError
23 from distutils
.ccompiler
import CCompiler
, gen_preprocess_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 VS_BASE
= r
"Software\Microsoft\VisualStudio\%0.1f"
41 WINSDK_BASE
= r
"Software\Microsoft\Microsoft SDKs\Windows"
42 NET_BASE
= r
"Software\Microsoft\.NETFramework"
44 # A map keyed by get_platform() return values to values accepted by
45 # 'vcvarsall.bat'. Note a cross-compile may combine these (eg, 'x86_amd64' is
46 # the param to cross-compile on x86 targetting amd64.)
49 'win-amd64' : 'amd64',
54 """Helper class to read values from the registry
57 def get_value(cls
, path
, key
):
59 d
= cls
.read_values(base
, path
)
63 get_value
= classmethod(get_value
)
65 def read_keys(cls
, base
, key
):
66 """Return list of registry keys."""
68 handle
= RegOpenKeyEx(base
, key
)
75 k
= RegEnumKey(handle
, i
)
81 read_keys
= classmethod(read_keys
)
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
)
103 read_values
= classmethod(read_values
)
106 dec
= getattr(s
, "decode", None)
113 convert_mbcs
= staticmethod(convert_mbcs
)
117 def __init__(self
, version
):
119 self
.vsbase
= VS_BASE
% version
120 self
.load_macros(version
)
122 def set_macro(self
, macro
, path
, key
):
123 self
.macros
["$(%s)" % macro
] = Reg
.get_value(path
, key
)
125 def load_macros(self
, version
):
126 self
.set_macro("VCInstallDir", self
.vsbase
+ r
"\Setup\VC", "productdir")
127 self
.set_macro("VSInstallDir", self
.vsbase
+ r
"\Setup\VS", "productdir")
128 self
.set_macro("FrameworkDir", NET_BASE
, "installroot")
131 self
.set_macro("FrameworkSDKDir", NET_BASE
,
132 "sdkinstallrootv2.0")
134 raise KeyError("sdkinstallrootv2.0")
136 raise DistutilsPlatformError(
137 """Python was built with Visual Studio 2008;
138 extensions must be built with a compiler than can generate compatible binaries.
139 Visual Studio 2008 was not found on this system. If you have Cygwin installed,
140 you can try compiling with MingW32, by passing "-c mingw32" to setup.py.""")
143 self
.set_macro("FrameworkVersion", self
.vsbase
, "clr version")
144 self
.set_macro("WindowsSdkDir", WINSDK_BASE
, "currentinstallfolder")
146 p
= r
"Software\Microsoft\NET Framework Setup\Product"
149 h
= RegOpenKeyEx(base
, p
)
152 key
= RegEnumKey(h
, 0)
153 d
= Reg
.get_value(base
, r
"%s\%s" % (p
, key
))
154 self
.macros
["$(FrameworkVersion)"] = d
["version"]
157 for k
, v
in self
.macros
.items():
161 def get_build_version():
162 """Return the version of MSVC that was used to build Python.
164 For Python 2.3 and up, the version number is included in
165 sys.version. For earlier versions, assume the compiler is MSVC 6.
168 i
= sys
.version
.find(prefix
)
172 s
, rest
= sys
.version
[i
:].split(" ", 1)
173 majorVersion
= int(s
[:-2]) - 6
174 minorVersion
= int(s
[2:3]) / 10.0
175 # I don't think paths are affected by minor version in version 6
176 if majorVersion
== 6:
178 if majorVersion
>= 6:
179 return majorVersion
+ minorVersion
180 # else we don't know what version of the compiler this is
183 def normalize_and_reduce_paths(paths
):
184 """Return a list of normalized paths with duplicates removed.
186 The current order of paths is maintained.
188 # Paths are normalized so things like: /a and /a/ aren't both preserved.
191 np
= os
.path
.normpath(p
)
192 # XXX(nnorwitz): O(n**2), if reduced_paths gets long perhaps use a set.
193 if np
not in reduced_paths
:
194 reduced_paths
.append(np
)
197 def removeDuplicates(variable
):
198 """Remove duplicate values of an environment variable.
200 oldList
= variable
.split(os
.pathsep
)
205 newVariable
= os
.pathsep
.join(newList
)
208 def find_vcvarsall(version
):
209 """Find the vcvarsall.bat file
211 At first it tries to find the productdir of VS 2008 in the registry. If
212 that fails it falls back to the VS90COMNTOOLS env var.
214 vsbase
= VS_BASE
% version
216 productdir
= Reg
.get_value(r
"%s\Setup\VC" % vsbase
,
219 log
.debug("Unable to find productdir in registry")
222 if not productdir
or not os
.path
.isdir(productdir
):
223 toolskey
= "VS%0.f0COMNTOOLS" % version
224 toolsdir
= os
.environ
.get(toolskey
, None)
226 if toolsdir
and os
.path
.isdir(toolsdir
):
227 productdir
= os
.path
.join(toolsdir
, os
.pardir
, os
.pardir
, "VC")
228 productdir
= os
.path
.abspath(productdir
)
229 if not os
.path
.isdir(productdir
):
230 log
.debug("%s is not a valid directory" % productdir
)
233 log
.debug("Env var %s is not set or invalid" % toolskey
)
235 log
.debug("No productdir found")
237 vcvarsall
= os
.path
.join(productdir
, "vcvarsall.bat")
238 if os
.path
.isfile(vcvarsall
):
240 log
.debug("Unable to find vcvarsall.bat")
243 def query_vcvarsall(version
, arch
="x86"):
244 """Launch vcvarsall.bat and read the settings from its environment
246 vcvarsall
= find_vcvarsall(version
)
247 interesting
= set(("include", "lib", "libpath", "path"))
250 if vcvarsall
is None:
251 raise DistutilsPlatformError("Unable to find vcvarsall.bat")
252 log
.debug("Calling 'vcvarsall.bat %s' (version=%s)", arch
, version
)
253 popen
= subprocess
.Popen('"%s" %s & set' % (vcvarsall
, arch
),
254 stdout
=subprocess
.PIPE
,
255 stderr
=subprocess
.PIPE
)
257 stdout
, stderr
= popen
.communicate()
258 if popen
.wait() != 0:
259 raise DistutilsPlatformError(stderr
.decode("mbcs"))
261 stdout
= stdout
.decode("mbcs")
262 for line
in stdout
.split("\n"):
263 line
= Reg
.convert_mbcs(line
)
267 key
, value
= line
.split('=', 1)
269 if key
in interesting
:
270 if value
.endswith(os
.pathsep
):
272 result
[key
] = removeDuplicates(value
)
274 if len(result
) != len(interesting
):
275 raise ValueError(str(list(result
.keys())))
280 VERSION
= get_build_version()
282 raise DistutilsPlatformError("VC %0.1f is not supported by this module" % VERSION
)
283 # MACROS = MacroExpander(VERSION)
285 class MSVCCompiler(CCompiler
) :
286 """Concrete class that implements an interface to Microsoft Visual C++,
287 as defined by the CCompiler abstract class."""
289 compiler_type
= 'msvc'
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.
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']
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'
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
.__root
= r
"Software\Microsoft\VisualStudio"
319 # self.__macros = MACROS
321 # target platform (.plat_name is consistent with 'bdist')
322 self
.plat_name
= None
323 self
.__arch
= None # deprecated name
324 self
.initialized
= False
326 def initialize(self
, plat_name
=None):
327 # multi-init means we would need to check platform same each time...
328 assert not self
.initialized
, "don't init multiple times"
329 if plat_name
is None:
330 plat_name
= get_platform()
331 # sanity check for platforms to prevent obscure errors later.
332 ok_plats
= 'win32', 'win-amd64', 'win-ia64'
333 if plat_name
not in ok_plats
:
334 raise DistutilsPlatformError("--plat-name must be one of %s" %
337 if "DISTUTILS_USE_SDK" in os
.environ
and "MSSdk" in os
.environ
and self
.find_exe("cl.exe"):
338 # Assume that the SDK set up everything alright; don't try to be
341 self
.linker
= "link.exe"
346 # On x86, 'vcvars32.bat amd64' creates an env that doesn't work;
347 # to cross compile, you use 'x86_amd64'.
348 # On AMD64, 'vcvars32.bat amd64' is a native build env; to cross
349 # compile use 'x86' (ie, it runs the x86 compiler directly)
350 # No idea how itanium handles this, if at all.
351 if plat_name
== get_platform() or plat_name
== 'win32':
352 # native build or cross-compile to win32
353 plat_spec
= PLAT_TO_VCVARS
[plat_name
]
355 # cross compile from win32 -> some 64bit
356 plat_spec
= PLAT_TO_VCVARS
[get_platform()] + '_' + \
357 PLAT_TO_VCVARS
[plat_name
]
359 vc_env
= query_vcvarsall(VERSION
, plat_spec
)
361 # take care to only use strings in the environment.
362 self
.__paths
= vc_env
['path'].encode('mbcs').split(os
.pathsep
)
363 os
.environ
['lib'] = vc_env
['lib'].encode('mbcs')
364 os
.environ
['include'] = vc_env
['include'].encode('mbcs')
366 if len(self
.__paths
) == 0:
367 raise DistutilsPlatformError("Python was built with %s, "
368 "and extensions need to be built with the same "
369 "version of the compiler, but it isn't installed."
372 self
.cc
= self
.find_exe("cl.exe")
373 self
.linker
= self
.find_exe("link.exe")
374 self
.lib
= self
.find_exe("lib.exe")
375 self
.rc
= self
.find_exe("rc.exe") # resource compiler
376 self
.mc
= self
.find_exe("mc.exe") # message compiler
377 #self.set_path_env_var('lib')
378 #self.set_path_env_var('include')
380 # extend the MSVC path with the current path
382 for p
in os
.environ
['path'].split(';'):
383 self
.__paths
.append(p
)
386 self
.__paths
= normalize_and_reduce_paths(self
.__paths
)
387 os
.environ
['path'] = ";".join(self
.__paths
)
389 self
.preprocess_options
= None
390 if self
.__arch
== "x86":
391 self
.compile_options
= [ '/nologo', '/Ox', '/MD', '/W3',
393 self
.compile_options_debug
= ['/nologo', '/Od', '/MDd', '/W3',
397 self
.compile_options
= [ '/nologo', '/Ox', '/MD', '/W3', '/GS-' ,
399 self
.compile_options_debug
= ['/nologo', '/Od', '/MDd', '/W3', '/GS-',
402 self
.ldflags_shared
= ['/DLL', '/nologo', '/INCREMENTAL:NO']
403 if self
.__version
>= 7:
404 self
.ldflags_shared_debug
= [
405 '/DLL', '/nologo', '/INCREMENTAL:no', '/DEBUG', '/pdb:None'
407 self
.ldflags_static
= [ '/nologo']
409 self
.initialized
= True
411 # -- Worker methods ------------------------------------------------
413 def object_filenames(self
,
417 # Copied from ccompiler.py, extended to return .res as 'object'-file
419 if output_dir
is None: output_dir
= ''
421 for src_name
in source_filenames
:
422 (base
, ext
) = os
.path
.splitext (src_name
)
423 base
= os
.path
.splitdrive(base
)[1] # Chop off the drive
424 base
= base
[os
.path
.isabs(base
):] # If abs, chop off leading /
425 if ext
not in self
.src_extensions
:
426 # Better to raise an exception instead of silently continuing
427 # and later complain about sources and targets having
429 raise CompileError ("Don't know how to compile %s" % src_name
)
431 base
= os
.path
.basename (base
)
432 if ext
in self
._rc
_extensions
:
433 obj_names
.append (os
.path
.join (output_dir
,
434 base
+ self
.res_extension
))
435 elif ext
in self
._mc
_extensions
:
436 obj_names
.append (os
.path
.join (output_dir
,
437 base
+ self
.res_extension
))
439 obj_names
.append (os
.path
.join (output_dir
,
440 base
+ self
.obj_extension
))
444 def compile(self
, sources
,
445 output_dir
=None, macros
=None, include_dirs
=None, debug
=0,
446 extra_preargs
=None, extra_postargs
=None, depends
=None):
448 if not self
.initialized
:
450 compile_info
= self
._setup
_compile
(output_dir
, macros
, include_dirs
,
451 sources
, depends
, extra_postargs
)
452 macros
, objects
, extra_postargs
, pp_opts
, build
= compile_info
454 compile_opts
= extra_preargs
or []
455 compile_opts
.append ('/c')
457 compile_opts
.extend(self
.compile_options_debug
)
459 compile_opts
.extend(self
.compile_options
)
463 src
, ext
= build
[obj
]
467 # pass the full pathname to MSVC in debug mode,
468 # this allows the debugger to find the source file
469 # without asking the user to browse for it
470 src
= os
.path
.abspath(src
)
472 if ext
in self
._c
_extensions
:
473 input_opt
= "/Tc" + src
474 elif ext
in self
._cpp
_extensions
:
475 input_opt
= "/Tp" + src
476 elif ext
in self
._rc
_extensions
:
477 # compile .RC to .RES file
479 output_opt
= "/fo" + obj
481 self
.spawn([self
.rc
] + pp_opts
+
482 [output_opt
] + [input_opt
])
483 except DistutilsExecError
, msg
:
484 raise CompileError(msg
)
486 elif ext
in self
._mc
_extensions
:
487 # Compile .MC to .RC file to .RES file.
488 # * '-h dir' specifies the directory for the
489 # generated include file
490 # * '-r dir' specifies the target directory of the
491 # generated RC file and the binary message resource
494 # For now (since there are no options to change this),
495 # we use the source-directory for the include file and
496 # the build directory for the RC file and message
497 # resources. This works at least for win32all.
498 h_dir
= os
.path
.dirname(src
)
499 rc_dir
= os
.path
.dirname(obj
)
501 # first compile .MC to .RC and .H file
502 self
.spawn([self
.mc
] +
503 ['-h', h_dir
, '-r', rc_dir
] + [src
])
504 base
, _
= os
.path
.splitext (os
.path
.basename (src
))
505 rc_file
= os
.path
.join (rc_dir
, base
+ '.rc')
506 # then compile .RC to .RES file
507 self
.spawn([self
.rc
] +
508 ["/fo" + obj
] + [rc_file
])
510 except DistutilsExecError
, msg
:
511 raise CompileError(msg
)
514 # how to handle this file?
515 raise CompileError("Don't know how to compile %s to %s"
518 output_opt
= "/Fo" + obj
520 self
.spawn([self
.cc
] + compile_opts
+ pp_opts
+
521 [input_opt
, output_opt
] +
523 except DistutilsExecError
, msg
:
524 raise CompileError(msg
)
529 def create_static_lib(self
,
536 if not self
.initialized
:
538 (objects
, output_dir
) = self
._fix
_object
_args
(objects
, output_dir
)
539 output_filename
= self
.library_filename(output_libname
,
540 output_dir
=output_dir
)
542 if self
._need
_link
(objects
, output_filename
):
543 lib_args
= objects
+ ['/OUT:' + output_filename
]
545 pass # XXX what goes here?
547 self
.spawn([self
.lib
] + lib_args
)
548 except DistutilsExecError
, msg
:
551 log
.debug("skipping %s (up-to-date)", output_filename
)
561 runtime_library_dirs
=None,
569 if not self
.initialized
:
571 (objects
, output_dir
) = self
._fix
_object
_args
(objects
, output_dir
)
572 fixed_args
= self
._fix
_lib
_args
(libraries
, library_dirs
,
573 runtime_library_dirs
)
574 (libraries
, library_dirs
, runtime_library_dirs
) = fixed_args
576 if runtime_library_dirs
:
577 self
.warn ("I don't know what to do with 'runtime_library_dirs': "
578 + str (runtime_library_dirs
))
580 lib_opts
= gen_lib_options(self
,
581 library_dirs
, runtime_library_dirs
,
583 if output_dir
is not None:
584 output_filename
= os
.path
.join(output_dir
, output_filename
)
586 if self
._need
_link
(objects
, output_filename
):
587 if target_desc
== CCompiler
.EXECUTABLE
:
589 ldflags
= self
.ldflags_shared_debug
[1:]
591 ldflags
= self
.ldflags_shared
[1:]
594 ldflags
= self
.ldflags_shared_debug
596 ldflags
= self
.ldflags_shared
599 for sym
in (export_symbols
or []):
600 export_opts
.append("/EXPORT:" + sym
)
602 ld_args
= (ldflags
+ lib_opts
+ export_opts
+
603 objects
+ ['/OUT:' + output_filename
])
605 # The MSVC linker generates .lib and .exp files, which cannot be
606 # suppressed by any linker switches. The .lib files may even be
607 # needed! Make sure they are generated in the temporary build
608 # directory. Since they have different names for debug and release
609 # builds, they can go into the same directory.
610 build_temp
= os
.path
.dirname(objects
[0])
611 if export_symbols
is not None:
612 (dll_name
, dll_ext
) = os
.path
.splitext(
613 os
.path
.basename(output_filename
))
614 implib_file
= os
.path
.join(
616 self
.library_filename(dll_name
))
617 ld_args
.append ('/IMPLIB:' + implib_file
)
619 # Embedded manifests are recommended - see MSDN article titled
620 # "How to: Embed a Manifest Inside a C/C++ Application"
621 # (currently at http://msdn2.microsoft.com/en-us/library/ms235591(VS.80).aspx)
622 # Ask the linker to generate the manifest in the temp dir, so
623 # we can embed it later.
624 temp_manifest
= os
.path
.join(
626 os
.path
.basename(output_filename
) + ".manifest")
627 ld_args
.append('/MANIFESTFILE:' + temp_manifest
)
630 ld_args
[:0] = extra_preargs
632 ld_args
.extend(extra_postargs
)
634 self
.mkpath(os
.path
.dirname(output_filename
))
636 self
.spawn([self
.linker
] + ld_args
)
637 except DistutilsExecError
, msg
:
641 # XXX - this is somewhat fragile - if mt.exe fails, distutils
642 # will still consider the DLL up-to-date, but it will not have a
643 # manifest. Maybe we should link to a temp file? OTOH, that
644 # implies a build environment error that shouldn't go undetected.
645 if target_desc
== CCompiler
.EXECUTABLE
:
649 out_arg
= '-outputresource:%s;%s' % (output_filename
, mfid
)
651 self
.spawn(['mt.exe', '-nologo', '-manifest',
652 temp_manifest
, out_arg
])
653 except DistutilsExecError
, msg
:
656 log
.debug("skipping %s (up-to-date)", output_filename
)
659 # -- Miscellaneous methods -----------------------------------------
660 # These are all used by the 'gen_lib_options() function, in
663 def library_dir_option(self
, dir):
664 return "/LIBPATH:" + dir
666 def runtime_library_dir_option(self
, dir):
667 raise DistutilsPlatformError(
668 "don't know how to set runtime library search path for MSVC++")
670 def library_option(self
, lib
):
671 return self
.library_filename(lib
)
674 def find_library_file(self
, dirs
, lib
, debug
=0):
675 # Prefer a debugging library if found (and requested), but deal
676 # with it if we don't have one.
678 try_names
= [lib
+ "_d", lib
]
682 for name
in try_names
:
683 libfile
= os
.path
.join(dir, self
.library_filename (name
))
684 if os
.path
.exists(libfile
):
687 # Oops, didn't find it in *any* of 'dirs'
690 # Helper methods for using the MSVC registry settings
692 def find_exe(self
, exe
):
693 """Return path to an MSVC executable program.
695 Tries to find the program in several places: first, one of the
696 MSVC program search paths from the registry; next, the directories
697 in the PATH environment variable. If any of those work, return an
698 absolute path that is known to exist. If none of them work, just
699 return the original program name, 'exe'.
701 for p
in self
.__paths
:
702 fn
= os
.path
.join(os
.path
.abspath(p
), exe
)
703 if os
.path
.isfile(fn
):
706 # didn't find it; try existing path
707 for p
in os
.environ
['Path'].split(';'):
708 fn
= os
.path
.join(os
.path
.abspath(p
),exe
)
709 if os
.path
.isfile(fn
):