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)
17 from distutils
.errors
import (DistutilsExecError
, DistutilsPlatformError
,
18 CompileError
, LibError
, LinkError
)
19 from distutils
.ccompiler
import CCompiler
, gen_lib_options
20 from distutils
import log
29 RegOpenKeyEx
= _winreg
.OpenKeyEx
30 RegEnumKey
= _winreg
.EnumKey
31 RegEnumValue
= _winreg
.EnumValue
32 RegError
= _winreg
.error
41 RegOpenKeyEx
= win32api
.RegOpenKeyEx
42 RegEnumKey
= win32api
.RegEnumKey
43 RegEnumValue
= win32api
.RegEnumValue
44 RegError
= win32api
.error
47 log
.info("Warning: Can't read registry to find the "
48 "necessary compiler setting\n"
49 "Make sure that Python modules _winreg, "
50 "win32api or win32con are installed.")
54 HKEYS
= (hkey_mod
.HKEY_USERS
,
55 hkey_mod
.HKEY_CURRENT_USER
,
56 hkey_mod
.HKEY_LOCAL_MACHINE
,
57 hkey_mod
.HKEY_CLASSES_ROOT
)
59 def read_keys(base
, key
):
60 """Return list of registry keys."""
63 handle
= RegOpenKeyEx(base
, key
)
70 k
= RegEnumKey(handle
, i
)
77 def read_values(base
, key
):
78 """Return dict of registry keys and values.
80 All names are converted to lowercase.
83 handle
= RegOpenKeyEx(base
, key
)
90 name
, value
, type = RegEnumValue(handle
, i
)
94 d
[convert_mbcs(name
)] = convert_mbcs(value
)
99 enc
= getattr(s
, "encode", None)
109 def __init__(self
, version
):
111 self
.load_macros(version
)
113 def set_macro(self
, macro
, path
, key
):
115 d
= read_values(base
, path
)
117 self
.macros
["$(%s)" % macro
] = d
[key
]
120 def load_macros(self
, version
):
121 vsbase
= r
"Software\Microsoft\VisualStudio\%0.1f" % version
122 self
.set_macro("VCInstallDir", vsbase
+ r
"\Setup\VC", "productdir")
123 self
.set_macro("VSInstallDir", vsbase
+ r
"\Setup\VS", "productdir")
124 net
= r
"Software\Microsoft\.NETFramework"
125 self
.set_macro("FrameworkDir", net
, "installroot")
128 self
.set_macro("FrameworkSDKDir", net
, "sdkinstallrootv1.1")
130 self
.set_macro("FrameworkSDKDir", net
, "sdkinstallroot")
132 raise DistutilsPlatformError
, \
133 ("""Python was built with Visual Studio 2003;
134 extensions must be built with a compiler than can generate compatible binaries.
135 Visual Studio 2003 was not found on this system. If you have Cygwin installed,
136 you can try compiling with MingW32, by passing "-c mingw32" to setup.py.""")
138 p
= r
"Software\Microsoft\NET Framework Setup\Product"
141 h
= RegOpenKeyEx(base
, p
)
144 key
= RegEnumKey(h
, 0)
145 d
= read_values(base
, r
"%s\%s" % (p
, key
))
146 self
.macros
["$(FrameworkVersion)"] = d
["version"]
149 for k
, v
in self
.macros
.items():
150 s
= string
.replace(s
, k
, v
)
153 def get_build_version():
154 """Return the version of MSVC that was used to build Python.
156 For Python 2.3 and up, the version number is included in
157 sys.version. For earlier versions, assume the compiler is MSVC 6.
161 i
= string
.find(sys
.version
, prefix
)
165 s
, rest
= sys
.version
[i
:].split(" ", 1)
166 majorVersion
= int(s
[:-2]) - 6
167 minorVersion
= int(s
[2:3]) / 10.0
168 # I don't think paths are affected by minor version in version 6
169 if majorVersion
== 6:
171 if majorVersion
>= 6:
172 return majorVersion
+ minorVersion
173 # else we don't know what version of the compiler this is
176 def get_build_architecture():
177 """Return the processor architecture.
179 Possible results are "Intel", "Itanium", or "AMD64".
183 i
= string
.find(sys
.version
, prefix
)
186 j
= string
.find(sys
.version
, ")", i
)
187 return sys
.version
[i
+len(prefix
):j
]
189 def normalize_and_reduce_paths(paths
):
190 """Return a list of normalized paths with duplicates removed.
192 The current order of paths is maintained.
194 # Paths are normalized so things like: /a and /a/ aren't both preserved.
197 np
= os
.path
.normpath(p
)
198 # XXX(nnorwitz): O(n**2), if reduced_paths gets long perhaps use a set.
199 if np
not in reduced_paths
:
200 reduced_paths
.append(np
)
204 class MSVCCompiler (CCompiler
) :
205 """Concrete class that implements an interface to Microsoft Visual C++,
206 as defined by the CCompiler abstract class."""
208 compiler_type
= 'msvc'
210 # Just set this so CCompiler's constructor doesn't barf. We currently
211 # don't use the 'set_executables()' bureaucracy provided by CCompiler,
212 # as it really isn't necessary for this sort of single-compiler class.
213 # Would be nice to have a consistent interface with UnixCCompiler,
214 # though, so it's worth thinking about.
217 # Private class data (need to distinguish C from C++ source for compiler)
218 _c_extensions
= ['.c']
219 _cpp_extensions
= ['.cc', '.cpp', '.cxx']
220 _rc_extensions
= ['.rc']
221 _mc_extensions
= ['.mc']
223 # Needed for the filename generation methods provided by the
224 # base class, CCompiler.
225 src_extensions
= (_c_extensions
+ _cpp_extensions
+
226 _rc_extensions
+ _mc_extensions
)
227 res_extension
= '.res'
228 obj_extension
= '.obj'
229 static_lib_extension
= '.lib'
230 shared_lib_extension
= '.dll'
231 static_lib_format
= shared_lib_format
= '%s%s'
232 exe_extension
= '.exe'
234 def __init__ (self
, verbose
=0, dry_run
=0, force
=0):
235 CCompiler
.__init
__ (self
, verbose
, dry_run
, force
)
236 self
.__version
= get_build_version()
237 self
.__arch
= get_build_architecture()
238 if self
.__arch
== "Intel":
240 if self
.__version
>= 7:
241 self
.__root
= r
"Software\Microsoft\VisualStudio"
242 self
.__macros
= MacroExpander(self
.__version
)
244 self
.__root
= r
"Software\Microsoft\Devstudio"
245 self
.__product
= "Visual Studio version %s" % self
.__version
247 # Win64. Assume this was built with the platform SDK
248 self
.__product
= "Microsoft SDK compiler %s" % (self
.__version
+ 6)
250 self
.initialized
= False
252 def initialize(self
):
254 if "DISTUTILS_USE_SDK" in os
.environ
and "MSSdk" in os
.environ
and self
.find_exe("cl.exe"):
255 # Assume that the SDK set up everything alright; don't try to be
258 self
.linker
= "link.exe"
263 self
.__paths
= self
.get_msvc_paths("path")
265 if len (self
.__paths
) == 0:
266 raise DistutilsPlatformError
, \
267 ("Python was built with %s, "
268 "and extensions need to be built with the same "
269 "version of the compiler, but it isn't installed." % self
.__product
)
271 self
.cc
= self
.find_exe("cl.exe")
272 self
.linker
= self
.find_exe("link.exe")
273 self
.lib
= self
.find_exe("lib.exe")
274 self
.rc
= self
.find_exe("rc.exe") # resource compiler
275 self
.mc
= self
.find_exe("mc.exe") # message compiler
276 self
.set_path_env_var('lib')
277 self
.set_path_env_var('include')
279 # extend the MSVC path with the current path
281 for p
in string
.split(os
.environ
['path'], ';'):
282 self
.__paths
.append(p
)
285 self
.__paths
= normalize_and_reduce_paths(self
.__paths
)
286 os
.environ
['path'] = string
.join(self
.__paths
, ';')
288 self
.preprocess_options
= None
289 if self
.__arch
== "Intel":
290 self
.compile_options
= [ '/nologo', '/Ox', '/MD', '/W3', '/GX' ,
292 self
.compile_options_debug
= ['/nologo', '/Od', '/MDd', '/W3', '/GX',
296 self
.compile_options
= [ '/nologo', '/Ox', '/MD', '/W3', '/GS-' ,
298 self
.compile_options_debug
= ['/nologo', '/Od', '/MDd', '/W3', '/GS-',
301 self
.ldflags_shared
= ['/DLL', '/nologo', '/INCREMENTAL:NO']
302 if self
.__version
>= 7:
303 self
.ldflags_shared_debug
= [
304 '/DLL', '/nologo', '/INCREMENTAL:no', '/DEBUG'
307 self
.ldflags_shared_debug
= [
308 '/DLL', '/nologo', '/INCREMENTAL:no', '/pdb:None', '/DEBUG'
310 self
.ldflags_static
= [ '/nologo']
312 self
.initialized
= True
314 # -- Worker methods ------------------------------------------------
316 def object_filenames (self
,
320 # Copied from ccompiler.py, extended to return .res as 'object'-file
322 if output_dir
is None: output_dir
= ''
324 for src_name
in source_filenames
:
325 (base
, ext
) = os
.path
.splitext (src_name
)
326 base
= os
.path
.splitdrive(base
)[1] # Chop off the drive
327 base
= base
[os
.path
.isabs(base
):] # If abs, chop off leading /
328 if ext
not in self
.src_extensions
:
329 # Better to raise an exception instead of silently continuing
330 # and later complain about sources and targets having
332 raise CompileError ("Don't know how to compile %s" % src_name
)
334 base
= os
.path
.basename (base
)
335 if ext
in self
._rc
_extensions
:
336 obj_names
.append (os
.path
.join (output_dir
,
337 base
+ self
.res_extension
))
338 elif ext
in self
._mc
_extensions
:
339 obj_names
.append (os
.path
.join (output_dir
,
340 base
+ self
.res_extension
))
342 obj_names
.append (os
.path
.join (output_dir
,
343 base
+ self
.obj_extension
))
346 # object_filenames ()
349 def compile(self
, sources
,
350 output_dir
=None, macros
=None, include_dirs
=None, debug
=0,
351 extra_preargs
=None, extra_postargs
=None, depends
=None):
353 if not self
.initialized
: self
.initialize()
354 macros
, objects
, extra_postargs
, pp_opts
, build
= \
355 self
._setup
_compile
(output_dir
, macros
, include_dirs
, sources
,
356 depends
, extra_postargs
)
358 compile_opts
= extra_preargs
or []
359 compile_opts
.append ('/c')
361 compile_opts
.extend(self
.compile_options_debug
)
363 compile_opts
.extend(self
.compile_options
)
367 src
, ext
= build
[obj
]
371 # pass the full pathname to MSVC in debug mode,
372 # this allows the debugger to find the source file
373 # without asking the user to browse for it
374 src
= os
.path
.abspath(src
)
376 if ext
in self
._c
_extensions
:
377 input_opt
= "/Tc" + src
378 elif ext
in self
._cpp
_extensions
:
379 input_opt
= "/Tp" + src
380 elif ext
in self
._rc
_extensions
:
381 # compile .RC to .RES file
383 output_opt
= "/fo" + obj
385 self
.spawn ([self
.rc
] + pp_opts
+
386 [output_opt
] + [input_opt
])
387 except DistutilsExecError
, msg
:
388 raise CompileError
, msg
390 elif ext
in self
._mc
_extensions
:
392 # Compile .MC to .RC file to .RES file.
393 # * '-h dir' specifies the directory for the
394 # generated include file
395 # * '-r dir' specifies the target directory of the
396 # generated RC file and the binary message resource
399 # For now (since there are no options to change this),
400 # we use the source-directory for the include file and
401 # the build directory for the RC file and message
402 # resources. This works at least for win32all.
404 h_dir
= os
.path
.dirname (src
)
405 rc_dir
= os
.path
.dirname (obj
)
407 # first compile .MC to .RC and .H file
408 self
.spawn ([self
.mc
] +
409 ['-h', h_dir
, '-r', rc_dir
] + [src
])
410 base
, _
= os
.path
.splitext (os
.path
.basename (src
))
411 rc_file
= os
.path
.join (rc_dir
, base
+ '.rc')
412 # then compile .RC to .RES file
413 self
.spawn ([self
.rc
] +
414 ["/fo" + obj
] + [rc_file
])
416 except DistutilsExecError
, msg
:
417 raise CompileError
, msg
420 # how to handle this file?
422 "Don't know how to compile %s to %s" % \
425 output_opt
= "/Fo" + obj
427 self
.spawn ([self
.cc
] + compile_opts
+ pp_opts
+
428 [input_opt
, output_opt
] +
430 except DistutilsExecError
, msg
:
431 raise CompileError
, msg
438 def create_static_lib (self
,
445 if not self
.initialized
: self
.initialize()
446 (objects
, output_dir
) = self
._fix
_object
_args
(objects
, output_dir
)
448 self
.library_filename (output_libname
, output_dir
=output_dir
)
450 if self
._need
_link
(objects
, output_filename
):
451 lib_args
= objects
+ ['/OUT:' + output_filename
]
453 pass # XXX what goes here?
455 self
.spawn ([self
.lib
] + lib_args
)
456 except DistutilsExecError
, msg
:
460 log
.debug("skipping %s (up-to-date)", output_filename
)
462 # create_static_lib ()
471 runtime_library_dirs
=None,
479 if not self
.initialized
: self
.initialize()
480 (objects
, output_dir
) = self
._fix
_object
_args
(objects
, output_dir
)
481 (libraries
, library_dirs
, runtime_library_dirs
) = \
482 self
._fix
_lib
_args
(libraries
, library_dirs
, runtime_library_dirs
)
484 if runtime_library_dirs
:
485 self
.warn ("I don't know what to do with 'runtime_library_dirs': "
486 + str (runtime_library_dirs
))
488 lib_opts
= gen_lib_options (self
,
489 library_dirs
, runtime_library_dirs
,
491 if output_dir
is not None:
492 output_filename
= os
.path
.join (output_dir
, output_filename
)
494 if self
._need
_link
(objects
, output_filename
):
496 if target_desc
== CCompiler
.EXECUTABLE
:
498 ldflags
= self
.ldflags_shared_debug
[1:]
500 ldflags
= self
.ldflags_shared
[1:]
503 ldflags
= self
.ldflags_shared_debug
505 ldflags
= self
.ldflags_shared
508 for sym
in (export_symbols
or []):
509 export_opts
.append("/EXPORT:" + sym
)
511 ld_args
= (ldflags
+ lib_opts
+ export_opts
+
512 objects
+ ['/OUT:' + output_filename
])
514 # The MSVC linker generates .lib and .exp files, which cannot be
515 # suppressed by any linker switches. The .lib files may even be
516 # needed! Make sure they are generated in the temporary build
517 # directory. Since they have different names for debug and release
518 # builds, they can go into the same directory.
519 if export_symbols
is not None:
520 (dll_name
, dll_ext
) = os
.path
.splitext(
521 os
.path
.basename(output_filename
))
522 implib_file
= os
.path
.join(
523 os
.path
.dirname(objects
[0]),
524 self
.library_filename(dll_name
))
525 ld_args
.append ('/IMPLIB:' + implib_file
)
528 ld_args
[:0] = extra_preargs
530 ld_args
.extend(extra_postargs
)
532 self
.mkpath (os
.path
.dirname (output_filename
))
534 self
.spawn ([self
.linker
] + ld_args
)
535 except DistutilsExecError
, msg
:
539 log
.debug("skipping %s (up-to-date)", output_filename
)
544 # -- Miscellaneous methods -----------------------------------------
545 # These are all used by the 'gen_lib_options() function, in
548 def library_dir_option (self
, dir):
549 return "/LIBPATH:" + dir
551 def runtime_library_dir_option (self
, dir):
552 raise DistutilsPlatformError
, \
553 "don't know how to set runtime library search path for MSVC++"
555 def library_option (self
, lib
):
556 return self
.library_filename (lib
)
559 def find_library_file (self
, dirs
, lib
, debug
=0):
560 # Prefer a debugging library if found (and requested), but deal
561 # with it if we don't have one.
563 try_names
= [lib
+ "_d", lib
]
567 for name
in try_names
:
568 libfile
= os
.path
.join(dir, self
.library_filename (name
))
569 if os
.path
.exists(libfile
):
572 # Oops, didn't find it in *any* of 'dirs'
575 # find_library_file ()
577 # Helper methods for using the MSVC registry settings
579 def find_exe(self
, exe
):
580 """Return path to an MSVC executable program.
582 Tries to find the program in several places: first, one of the
583 MSVC program search paths from the registry; next, the directories
584 in the PATH environment variable. If any of those work, return an
585 absolute path that is known to exist. If none of them work, just
586 return the original program name, 'exe'.
589 for p
in self
.__paths
:
590 fn
= os
.path
.join(os
.path
.abspath(p
), exe
)
591 if os
.path
.isfile(fn
):
594 # didn't find it; try existing path
595 for p
in string
.split(os
.environ
['Path'],';'):
596 fn
= os
.path
.join(os
.path
.abspath(p
),exe
)
597 if os
.path
.isfile(fn
):
602 def get_msvc_paths(self
, path
, platform
='x86'):
603 """Get a list of devstudio directories (include, lib or path).
605 Return a list of strings. The list will be empty if unable to
606 access the registry or appropriate registry keys not found.
609 if not _can_read_reg
:
612 path
= path
+ " dirs"
613 if self
.__version
>= 7:
614 key
= (r
"%s\%0.1f\VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories"
615 % (self
.__root
, self
.__version
))
617 key
= (r
"%s\6.0\Build System\Components\Platforms"
618 r
"\Win32 (%s)\Directories" % (self
.__root
, platform
))
621 d
= read_values(base
, key
)
623 if self
.__version
>= 7:
624 return string
.split(self
.__macros
.sub(d
[path
]), ";")
626 return string
.split(d
[path
], ";")
627 # MSVC 6 seems to create the registry entries we need only when
629 if self
.__version
== 6:
631 if read_values(base
, r
"%s\6.0" % self
.__root
) is not None:
632 self
.warn("It seems you have Visual Studio 6 installed, "
633 "but the expected registry settings are not present.\n"
634 "You must at least run the Visual Studio GUI once "
635 "so that these entries are created.")
639 def set_path_env_var(self
, name
):
640 """Set environment variable 'name' to an MSVC path type value.
642 This is equivalent to a SET command prior to execution of spawned
647 p
= self
.get_msvc_paths("library")
649 p
= self
.get_msvc_paths(name
)
651 os
.environ
[name
] = string
.join(p
, ';')
654 if get_build_version() >= 8.0:
655 log
.debug("Importing new compiler from distutils.msvc9compiler")
656 OldMSVCCompiler
= MSVCCompiler
657 from distutils
.msvc9compiler
import MSVCCompiler
658 # get_build_architecture not really relevant now we support cross-compile
659 from distutils
.msvc9compiler
import MacroExpander