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)
13 import sys
, os
, string
14 from distutils
.errors
import \
15 DistutilsExecError
, DistutilsPlatformError
, \
16 CompileError
, LibError
, LinkError
17 from distutils
.ccompiler
import \
18 CCompiler
, gen_preprocess_options
, gen_lib_options
19 from distutils
import log
28 RegOpenKeyEx
= _winreg
.OpenKeyEx
29 RegEnumKey
= _winreg
.EnumKey
30 RegEnumValue
= _winreg
.EnumValue
31 RegError
= _winreg
.error
40 RegOpenKeyEx
= win32api
.RegOpenKeyEx
41 RegEnumKey
= win32api
.RegEnumKey
42 RegEnumValue
= win32api
.RegEnumValue
43 RegError
= win32api
.error
46 log
.info("Warning: Can't read registry to find the "
47 "necessary compiler setting\n"
48 "Make sure that Python modules _winreg, "
49 "win32api or win32con are installed.")
53 HKEYS
= (hkey_mod
.HKEY_USERS
,
54 hkey_mod
.HKEY_CURRENT_USER
,
55 hkey_mod
.HKEY_LOCAL_MACHINE
,
56 hkey_mod
.HKEY_CLASSES_ROOT
)
58 def read_keys(base
, key
):
59 """Return list of registry keys."""
62 handle
= RegOpenKeyEx(base
, key
)
69 k
= RegEnumKey(handle
, i
)
76 def read_values(base
, key
):
77 """Return dict of registry keys and values.
79 All names are converted to lowercase.
82 handle
= RegOpenKeyEx(base
, key
)
89 name
, value
, type = RegEnumValue(handle
, i
)
93 d
[convert_mbcs(name
)] = convert_mbcs(value
)
98 enc
= getattr(s
, "encode", None)
108 def __init__(self
, version
):
110 self
.load_macros(version
)
112 def set_macro(self
, macro
, path
, key
):
114 d
= read_values(base
, path
)
116 self
.macros
["$(%s)" % macro
] = d
[key
]
119 def load_macros(self
, version
):
120 vsbase
= r
"Software\Microsoft\VisualStudio\%0.1f" % version
121 self
.set_macro("VCInstallDir", vsbase
+ r
"\Setup\VC", "productdir")
122 self
.set_macro("VSInstallDir", vsbase
+ r
"\Setup\VS", "productdir")
123 net
= r
"Software\Microsoft\.NETFramework"
124 self
.set_macro("FrameworkDir", net
, "installroot")
127 self
.set_macro("FrameworkSDKDir", net
, "sdkinstallrootv1.1")
129 self
.set_macro("FrameworkSDKDir", net
, "sdkinstallroot")
130 except KeyError, exc
: #
131 raise DistutilsPlatformError
, \
132 ("""Python was built with Visual Studio 2003;
133 extensions must be built with a compiler than can generate compatible binaries.
134 Visual Studio 2003 was not found on this system. If you have Cygwin installed,
135 you can try compiling with MingW32, by passing "-c mingw32" to setup.py.""")
137 p
= r
"Software\Microsoft\NET Framework Setup\Product"
140 h
= RegOpenKeyEx(base
, p
)
143 key
= RegEnumKey(h
, 0)
144 d
= read_values(base
, r
"%s\%s" % (p
, key
))
145 self
.macros
["$(FrameworkVersion)"] = d
["version"]
148 for k
, v
in self
.macros
.items():
149 s
= string
.replace(s
, k
, v
)
152 def get_build_version():
153 """Return the version of MSVC that was used to build Python.
155 For Python 2.3 and up, the version number is included in
156 sys.version. For earlier versions, assume the compiler is MSVC 6.
160 i
= string
.find(sys
.version
, prefix
)
164 s
, rest
= sys
.version
[i
:].split(" ", 1)
165 majorVersion
= int(s
[:-2]) - 6
166 minorVersion
= int(s
[2:3]) / 10.0
167 # I don't think paths are affected by minor version in version 6
168 if majorVersion
== 6:
170 if majorVersion
>= 6:
171 return majorVersion
+ minorVersion
172 # else we don't know what version of the compiler this is
175 def get_build_architecture():
176 """Return the processor architecture.
178 Possible results are "Intel", "Itanium", or "AMD64".
182 i
= string
.find(sys
.version
, prefix
)
185 j
= string
.find(sys
.version
, ")", i
)
186 return sys
.version
[i
+len(prefix
):j
]
188 def normalize_and_reduce_paths(paths
):
189 """Return a list of normalized paths with duplicates removed.
191 The current order of paths is maintained.
193 # Paths are normalized so things like: /a and /a/ aren't both preserved.
196 np
= os
.path
.normpath(p
)
197 # XXX(nnorwitz): O(n**2), if reduced_paths gets long perhaps use a set.
198 if np
not in reduced_paths
:
199 reduced_paths
.append(np
)
203 class MSVCCompiler (CCompiler
) :
204 """Concrete class that implements an interface to Microsoft Visual C++,
205 as defined by the CCompiler abstract class."""
207 compiler_type
= 'msvc'
209 # Just set this so CCompiler's constructor doesn't barf. We currently
210 # don't use the 'set_executables()' bureaucracy provided by CCompiler,
211 # as it really isn't necessary for this sort of single-compiler class.
212 # Would be nice to have a consistent interface with UnixCCompiler,
213 # though, so it's worth thinking about.
216 # Private class data (need to distinguish C from C++ source for compiler)
217 _c_extensions
= ['.c']
218 _cpp_extensions
= ['.cc', '.cpp', '.cxx']
219 _rc_extensions
= ['.rc']
220 _mc_extensions
= ['.mc']
222 # Needed for the filename generation methods provided by the
223 # base class, CCompiler.
224 src_extensions
= (_c_extensions
+ _cpp_extensions
+
225 _rc_extensions
+ _mc_extensions
)
226 res_extension
= '.res'
227 obj_extension
= '.obj'
228 static_lib_extension
= '.lib'
229 shared_lib_extension
= '.dll'
230 static_lib_format
= shared_lib_format
= '%s%s'
231 exe_extension
= '.exe'
233 def __init__ (self
, verbose
=0, dry_run
=0, force
=0):
234 CCompiler
.__init
__ (self
, verbose
, dry_run
, force
)
235 self
.__version
= get_build_version()
236 self
.__arch
= get_build_architecture()
237 if self
.__arch
== "Intel":
239 if self
.__version
>= 7:
240 self
.__root
= r
"Software\Microsoft\VisualStudio"
241 self
.__macros
= MacroExpander(self
.__version
)
243 self
.__root
= r
"Software\Microsoft\Devstudio"
244 self
.__product
= "Visual Studio version %s" % self
.__version
246 # Win64. Assume this was built with the platform SDK
247 self
.__product
= "Microsoft SDK compiler %s" % (self
.__version
+ 6)
249 self
.initialized
= False
251 def initialize(self
):
253 if "DISTUTILS_USE_SDK" in os
.environ
and "MSSdk" in os
.environ
and self
.find_exe("cl.exe"):
254 # Assume that the SDK set up everything alright; don't try to be
257 self
.linker
= "link.exe"
262 self
.__paths
= self
.get_msvc_paths("path")
264 if len (self
.__paths
) == 0:
265 raise DistutilsPlatformError
, \
266 ("Python was built with %s, "
267 "and extensions need to be built with the same "
268 "version of the compiler, but it isn't installed." % self
.__product
)
270 self
.cc
= self
.find_exe("cl.exe")
271 self
.linker
= self
.find_exe("link.exe")
272 self
.lib
= self
.find_exe("lib.exe")
273 self
.rc
= self
.find_exe("rc.exe") # resource compiler
274 self
.mc
= self
.find_exe("mc.exe") # message compiler
275 self
.set_path_env_var('lib')
276 self
.set_path_env_var('include')
278 # extend the MSVC path with the current path
280 for p
in string
.split(os
.environ
['path'], ';'):
281 self
.__paths
.append(p
)
284 self
.__paths
= normalize_and_reduce_paths(self
.__paths
)
285 os
.environ
['path'] = string
.join(self
.__paths
, ';')
287 self
.preprocess_options
= None
288 if self
.__arch
== "Intel":
289 self
.compile_options
= [ '/nologo', '/Ox', '/MD', '/W3', '/GX' ,
291 self
.compile_options_debug
= ['/nologo', '/Od', '/MDd', '/W3', '/GX',
295 self
.compile_options
= [ '/nologo', '/Ox', '/MD', '/W3', '/GS-' ,
297 self
.compile_options_debug
= ['/nologo', '/Od', '/MDd', '/W3', '/GS-',
300 self
.ldflags_shared
= ['/DLL', '/nologo', '/INCREMENTAL:NO']
301 if self
.__version
>= 7:
302 self
.ldflags_shared_debug
= [
303 '/DLL', '/nologo', '/INCREMENTAL:no', '/DEBUG'
306 self
.ldflags_shared_debug
= [
307 '/DLL', '/nologo', '/INCREMENTAL:no', '/pdb:None', '/DEBUG'
309 self
.ldflags_static
= [ '/nologo']
311 self
.initialized
= True
313 # -- Worker methods ------------------------------------------------
315 def object_filenames (self
,
319 # Copied from ccompiler.py, extended to return .res as 'object'-file
321 if output_dir
is None: output_dir
= ''
323 for src_name
in source_filenames
:
324 (base
, ext
) = os
.path
.splitext (src_name
)
325 base
= os
.path
.splitdrive(base
)[1] # Chop off the drive
326 base
= base
[os
.path
.isabs(base
):] # If abs, chop off leading /
327 if ext
not in self
.src_extensions
:
328 # Better to raise an exception instead of silently continuing
329 # and later complain about sources and targets having
331 raise CompileError ("Don't know how to compile %s" % src_name
)
333 base
= os
.path
.basename (base
)
334 if ext
in self
._rc
_extensions
:
335 obj_names
.append (os
.path
.join (output_dir
,
336 base
+ self
.res_extension
))
337 elif ext
in self
._mc
_extensions
:
338 obj_names
.append (os
.path
.join (output_dir
,
339 base
+ self
.res_extension
))
341 obj_names
.append (os
.path
.join (output_dir
,
342 base
+ self
.obj_extension
))
345 # object_filenames ()
348 def compile(self
, sources
,
349 output_dir
=None, macros
=None, include_dirs
=None, debug
=0,
350 extra_preargs
=None, extra_postargs
=None, depends
=None):
352 if not self
.initialized
: self
.initialize()
353 macros
, objects
, extra_postargs
, pp_opts
, build
= \
354 self
._setup
_compile
(output_dir
, macros
, include_dirs
, sources
,
355 depends
, extra_postargs
)
357 compile_opts
= extra_preargs
or []
358 compile_opts
.append ('/c')
360 compile_opts
.extend(self
.compile_options_debug
)
362 compile_opts
.extend(self
.compile_options
)
366 src
, ext
= build
[obj
]
370 # pass the full pathname to MSVC in debug mode,
371 # this allows the debugger to find the source file
372 # without asking the user to browse for it
373 src
= os
.path
.abspath(src
)
375 if ext
in self
._c
_extensions
:
376 input_opt
= "/Tc" + src
377 elif ext
in self
._cpp
_extensions
:
378 input_opt
= "/Tp" + src
379 elif ext
in self
._rc
_extensions
:
380 # compile .RC to .RES file
382 output_opt
= "/fo" + obj
384 self
.spawn ([self
.rc
] + pp_opts
+
385 [output_opt
] + [input_opt
])
386 except DistutilsExecError
, msg
:
387 raise CompileError
, msg
389 elif ext
in self
._mc
_extensions
:
391 # Compile .MC to .RC file to .RES file.
392 # * '-h dir' specifies the directory for the
393 # generated include file
394 # * '-r dir' specifies the target directory of the
395 # generated RC file and the binary message resource
398 # For now (since there are no options to change this),
399 # we use the source-directory for the include file and
400 # the build directory for the RC file and message
401 # resources. This works at least for win32all.
403 h_dir
= os
.path
.dirname (src
)
404 rc_dir
= os
.path
.dirname (obj
)
406 # first compile .MC to .RC and .H file
407 self
.spawn ([self
.mc
] +
408 ['-h', h_dir
, '-r', rc_dir
] + [src
])
409 base
, _
= os
.path
.splitext (os
.path
.basename (src
))
410 rc_file
= os
.path
.join (rc_dir
, base
+ '.rc')
411 # then compile .RC to .RES file
412 self
.spawn ([self
.rc
] +
413 ["/fo" + obj
] + [rc_file
])
415 except DistutilsExecError
, msg
:
416 raise CompileError
, msg
419 # how to handle this file?
421 "Don't know how to compile %s to %s" % \
424 output_opt
= "/Fo" + obj
426 self
.spawn ([self
.cc
] + compile_opts
+ pp_opts
+
427 [input_opt
, output_opt
] +
429 except DistutilsExecError
, msg
:
430 raise CompileError
, msg
437 def create_static_lib (self
,
444 if not self
.initialized
: self
.initialize()
445 (objects
, output_dir
) = self
._fix
_object
_args
(objects
, output_dir
)
447 self
.library_filename (output_libname
, output_dir
=output_dir
)
449 if self
._need
_link
(objects
, output_filename
):
450 lib_args
= objects
+ ['/OUT:' + output_filename
]
452 pass # XXX what goes here?
454 self
.spawn ([self
.lib
] + lib_args
)
455 except DistutilsExecError
, msg
:
459 log
.debug("skipping %s (up-to-date)", output_filename
)
461 # create_static_lib ()
470 runtime_library_dirs
=None,
478 if not self
.initialized
: self
.initialize()
479 (objects
, output_dir
) = self
._fix
_object
_args
(objects
, output_dir
)
480 (libraries
, library_dirs
, runtime_library_dirs
) = \
481 self
._fix
_lib
_args
(libraries
, library_dirs
, runtime_library_dirs
)
483 if runtime_library_dirs
:
484 self
.warn ("I don't know what to do with 'runtime_library_dirs': "
485 + str (runtime_library_dirs
))
487 lib_opts
= gen_lib_options (self
,
488 library_dirs
, runtime_library_dirs
,
490 if output_dir
is not None:
491 output_filename
= os
.path
.join (output_dir
, output_filename
)
493 if self
._need
_link
(objects
, output_filename
):
495 if target_desc
== CCompiler
.EXECUTABLE
:
497 ldflags
= self
.ldflags_shared_debug
[1:]
499 ldflags
= self
.ldflags_shared
[1:]
502 ldflags
= self
.ldflags_shared_debug
504 ldflags
= self
.ldflags_shared
507 for sym
in (export_symbols
or []):
508 export_opts
.append("/EXPORT:" + sym
)
510 ld_args
= (ldflags
+ lib_opts
+ export_opts
+
511 objects
+ ['/OUT:' + output_filename
])
513 # The MSVC linker generates .lib and .exp files, which cannot be
514 # suppressed by any linker switches. The .lib files may even be
515 # needed! Make sure they are generated in the temporary build
516 # directory. Since they have different names for debug and release
517 # builds, they can go into the same directory.
518 if export_symbols
is not None:
519 (dll_name
, dll_ext
) = os
.path
.splitext(
520 os
.path
.basename(output_filename
))
521 implib_file
= os
.path
.join(
522 os
.path
.dirname(objects
[0]),
523 self
.library_filename(dll_name
))
524 ld_args
.append ('/IMPLIB:' + implib_file
)
527 ld_args
[:0] = extra_preargs
529 ld_args
.extend(extra_postargs
)
531 self
.mkpath (os
.path
.dirname (output_filename
))
533 self
.spawn ([self
.linker
] + ld_args
)
534 except DistutilsExecError
, msg
:
538 log
.debug("skipping %s (up-to-date)", output_filename
)
543 # -- Miscellaneous methods -----------------------------------------
544 # These are all used by the 'gen_lib_options() function, in
547 def library_dir_option (self
, dir):
548 return "/LIBPATH:" + dir
550 def runtime_library_dir_option (self
, dir):
551 raise DistutilsPlatformError
, \
552 "don't know how to set runtime library search path for MSVC++"
554 def library_option (self
, lib
):
555 return self
.library_filename (lib
)
558 def find_library_file (self
, dirs
, lib
, debug
=0):
559 # Prefer a debugging library if found (and requested), but deal
560 # with it if we don't have one.
562 try_names
= [lib
+ "_d", lib
]
566 for name
in try_names
:
567 libfile
= os
.path
.join(dir, self
.library_filename (name
))
568 if os
.path
.exists(libfile
):
571 # Oops, didn't find it in *any* of 'dirs'
574 # find_library_file ()
576 # Helper methods for using the MSVC registry settings
578 def find_exe(self
, exe
):
579 """Return path to an MSVC executable program.
581 Tries to find the program in several places: first, one of the
582 MSVC program search paths from the registry; next, the directories
583 in the PATH environment variable. If any of those work, return an
584 absolute path that is known to exist. If none of them work, just
585 return the original program name, 'exe'.
588 for p
in self
.__paths
:
589 fn
= os
.path
.join(os
.path
.abspath(p
), exe
)
590 if os
.path
.isfile(fn
):
593 # didn't find it; try existing path
594 for p
in string
.split(os
.environ
['Path'],';'):
595 fn
= os
.path
.join(os
.path
.abspath(p
),exe
)
596 if os
.path
.isfile(fn
):
601 def get_msvc_paths(self
, path
, platform
='x86'):
602 """Get a list of devstudio directories (include, lib or path).
604 Return a list of strings. The list will be empty if unable to
605 access the registry or appropriate registry keys not found.
608 if not _can_read_reg
:
611 path
= path
+ " dirs"
612 if self
.__version
>= 7:
613 key
= (r
"%s\%0.1f\VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories"
614 % (self
.__root
, self
.__version
))
616 key
= (r
"%s\6.0\Build System\Components\Platforms"
617 r
"\Win32 (%s)\Directories" % (self
.__root
, platform
))
620 d
= read_values(base
, key
)
622 if self
.__version
>= 7:
623 return string
.split(self
.__macros
.sub(d
[path
]), ";")
625 return string
.split(d
[path
], ";")
626 # MSVC 6 seems to create the registry entries we need only when
628 if self
.__version
== 6:
630 if read_values(base
, r
"%s\6.0" % self
.__root
) is not None:
631 self
.warn("It seems you have Visual Studio 6 installed, "
632 "but the expected registry settings are not present.\n"
633 "You must at least run the Visual Studio GUI once "
634 "so that these entries are created.")
638 def set_path_env_var(self
, name
):
639 """Set environment variable 'name' to an MSVC path type value.
641 This is equivalent to a SET command prior to execution of spawned
646 p
= self
.get_msvc_paths("library")
648 p
= self
.get_msvc_paths(name
)
650 os
.environ
[name
] = string
.join(p
, ';')
653 if get_build_version() >= 8.0:
654 log
.debug("Importing new compiler from distutils.msvc9compiler")
655 OldMSVCCompiler
= MSVCCompiler
656 from distutils
.msvc9compiler
import MSVCCompiler
657 # get_build_architecture not really relevant now we support cross-compile
658 from distutils
.msvc9compiler
import MacroExpander