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)
11 # This module should be kept compatible with Python 2.1.
15 import sys
, os
, string
16 from distutils
.errors
import \
17 DistutilsExecError
, DistutilsPlatformError
, \
18 CompileError
, LibError
, LinkError
19 from distutils
.ccompiler
import \
20 CCompiler
, gen_preprocess_options
, gen_lib_options
21 from distutils
import log
30 RegOpenKeyEx
= _winreg
.OpenKeyEx
31 RegEnumKey
= _winreg
.EnumKey
32 RegEnumValue
= _winreg
.EnumValue
33 RegError
= _winreg
.error
42 RegOpenKeyEx
= win32api
.RegOpenKeyEx
43 RegEnumKey
= win32api
.RegEnumKey
44 RegEnumValue
= win32api
.RegEnumValue
45 RegError
= win32api
.error
48 log
.info("Warning: Can't read registry to find the "
49 "necessary compiler setting\n"
50 "Make sure that Python modules _winreg, "
51 "win32api or win32con are installed.")
55 HKEYS
= (hkey_mod
.HKEY_USERS
,
56 hkey_mod
.HKEY_CURRENT_USER
,
57 hkey_mod
.HKEY_LOCAL_MACHINE
,
58 hkey_mod
.HKEY_CLASSES_ROOT
)
60 def read_keys(base
, key
):
61 """Return list of registry keys."""
64 handle
= RegOpenKeyEx(base
, key
)
71 k
= RegEnumKey(handle
, i
)
78 def read_values(base
, key
):
79 """Return dict of registry keys and values.
81 All names are converted to lowercase.
84 handle
= RegOpenKeyEx(base
, key
)
91 name
, value
, type = RegEnumValue(handle
, i
)
95 d
[convert_mbcs(name
)] = convert_mbcs(value
)
100 enc
= getattr(s
, "encode", None)
110 def __init__(self
, version
):
112 self
.load_macros(version
)
114 def set_macro(self
, macro
, path
, key
):
116 d
= read_values(base
, path
)
118 self
.macros
["$(%s)" % macro
] = d
[key
]
121 def load_macros(self
, version
):
122 vsbase
= r
"Software\Microsoft\VisualStudio\%0.1f" % version
123 self
.set_macro("VCInstallDir", vsbase
+ r
"\Setup\VC", "productdir")
124 self
.set_macro("VSInstallDir", vsbase
+ r
"\Setup\VS", "productdir")
125 net
= r
"Software\Microsoft\.NETFramework"
126 self
.set_macro("FrameworkDir", net
, "installroot")
129 self
.set_macro("FrameworkSDKDir", net
, "sdkinstallrootv1.1")
131 self
.set_macro("FrameworkSDKDir", net
, "sdkinstallroot")
132 except KeyError, exc
: #
133 raise DistutilsPlatformError
, \
134 ("The .NET Framework SDK needs to be installed before "
135 "building extensions for Python.")
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
]
190 class MSVCCompiler (CCompiler
) :
191 """Concrete class that implements an interface to Microsoft Visual C++,
192 as defined by the CCompiler abstract class."""
194 compiler_type
= 'msvc'
196 # Just set this so CCompiler's constructor doesn't barf. We currently
197 # don't use the 'set_executables()' bureaucracy provided by CCompiler,
198 # as it really isn't necessary for this sort of single-compiler class.
199 # Would be nice to have a consistent interface with UnixCCompiler,
200 # though, so it's worth thinking about.
203 # Private class data (need to distinguish C from C++ source for compiler)
204 _c_extensions
= ['.c']
205 _cpp_extensions
= ['.cc', '.cpp', '.cxx']
206 _rc_extensions
= ['.rc']
207 _mc_extensions
= ['.mc']
209 # Needed for the filename generation methods provided by the
210 # base class, CCompiler.
211 src_extensions
= (_c_extensions
+ _cpp_extensions
+
212 _rc_extensions
+ _mc_extensions
)
213 res_extension
= '.res'
214 obj_extension
= '.obj'
215 static_lib_extension
= '.lib'
216 shared_lib_extension
= '.dll'
217 static_lib_format
= shared_lib_format
= '%s%s'
218 exe_extension
= '.exe'
220 def __init__ (self
, verbose
=0, dry_run
=0, force
=0):
221 CCompiler
.__init
__ (self
, verbose
, dry_run
, force
)
222 self
.__version
= get_build_version()
223 self
.__arch
= get_build_architecture()
224 if self
.__arch
== "Intel":
226 if self
.__version
>= 7:
227 self
.__root
= r
"Software\Microsoft\VisualStudio"
228 self
.__macros
= MacroExpander(self
.__version
)
230 self
.__root
= r
"Software\Microsoft\Devstudio"
231 self
.__product
= "Visual Studio version %s" % self
.__version
233 # Win64. Assume this was built with the platform SDK
234 self
.__product
= "Microsoft SDK compiler %s" % (self
.__version
+ 6)
236 self
.initialized
= False
238 def initialize(self
):
240 if os
.environ
.has_key("MSSdk") and self
.find_exe("cl.exe"):
241 # Assume that the SDK set up everything alright; don't try to be
244 self
.linker
= "link.exe"
249 self
.__paths
= self
.get_msvc_paths("path")
251 if len (self
.__paths
) == 0:
252 raise DistutilsPlatformError
, \
253 ("Python was built with %s, "
254 "and extensions need to be built with the same "
255 "version of the compiler, but it isn't installed." % self
.__product
)
257 self
.cc
= self
.find_exe("cl.exe")
258 self
.linker
= self
.find_exe("link.exe")
259 self
.lib
= self
.find_exe("lib.exe")
260 self
.rc
= self
.find_exe("rc.exe") # resource compiler
261 self
.mc
= self
.find_exe("mc.exe") # message compiler
262 self
.set_path_env_var('lib')
263 self
.set_path_env_var('include')
265 # extend the MSVC path with the current path
267 for p
in string
.split(os
.environ
['path'], ';'):
268 self
.__paths
.append(p
)
271 os
.environ
['path'] = string
.join(self
.__paths
, ';')
273 self
.preprocess_options
= None
274 if self
.__arch
== "Intel":
275 self
.compile_options
= [ '/nologo', '/Ox', '/MD', '/W3', '/GX' ,
277 self
.compile_options_debug
= ['/nologo', '/Od', '/MDd', '/W3', '/GX',
281 self
.compile_options
= [ '/nologo', '/Ox', '/MD', '/W3', '/GS-' ,
283 self
.compile_options_debug
= ['/nologo', '/Od', '/MDd', '/W3', '/GS-',
286 self
.ldflags_shared
= ['/DLL', '/nologo', '/INCREMENTAL:NO']
287 if self
.__version
>= 7:
288 self
.ldflags_shared_debug
= [
289 '/DLL', '/nologo', '/INCREMENTAL:no', '/DEBUG'
292 self
.ldflags_shared_debug
= [
293 '/DLL', '/nologo', '/INCREMENTAL:no', '/pdb:None', '/DEBUG'
295 self
.ldflags_static
= [ '/nologo']
297 self
.initialized
= True
299 # -- Worker methods ------------------------------------------------
301 def object_filenames (self
,
305 # Copied from ccompiler.py, extended to return .res as 'object'-file
307 if output_dir
is None: output_dir
= ''
309 for src_name
in source_filenames
:
310 (base
, ext
) = os
.path
.splitext (src_name
)
311 base
= os
.path
.splitdrive(base
)[1] # Chop off the drive
312 base
= base
[os
.path
.isabs(base
):] # If abs, chop off leading /
313 if ext
not in self
.src_extensions
:
314 # Better to raise an exception instead of silently continuing
315 # and later complain about sources and targets having
317 raise CompileError ("Don't know how to compile %s" % src_name
)
319 base
= os
.path
.basename (base
)
320 if ext
in self
._rc
_extensions
:
321 obj_names
.append (os
.path
.join (output_dir
,
322 base
+ self
.res_extension
))
323 elif ext
in self
._mc
_extensions
:
324 obj_names
.append (os
.path
.join (output_dir
,
325 base
+ self
.res_extension
))
327 obj_names
.append (os
.path
.join (output_dir
,
328 base
+ self
.obj_extension
))
331 # object_filenames ()
334 def compile(self
, sources
,
335 output_dir
=None, macros
=None, include_dirs
=None, debug
=0,
336 extra_preargs
=None, extra_postargs
=None, depends
=None):
338 if not self
.initialized
: self
.initialize()
339 macros
, objects
, extra_postargs
, pp_opts
, build
= \
340 self
._setup
_compile
(output_dir
, macros
, include_dirs
, sources
,
341 depends
, extra_postargs
)
343 compile_opts
= extra_preargs
or []
344 compile_opts
.append ('/c')
346 compile_opts
.extend(self
.compile_options_debug
)
348 compile_opts
.extend(self
.compile_options
)
352 src
, ext
= build
[obj
]
356 # pass the full pathname to MSVC in debug mode,
357 # this allows the debugger to find the source file
358 # without asking the user to browse for it
359 src
= os
.path
.abspath(src
)
361 if ext
in self
._c
_extensions
:
362 input_opt
= "/Tc" + src
363 elif ext
in self
._cpp
_extensions
:
364 input_opt
= "/Tp" + src
365 elif ext
in self
._rc
_extensions
:
366 # compile .RC to .RES file
368 output_opt
= "/fo" + obj
370 self
.spawn ([self
.rc
] + pp_opts
+
371 [output_opt
] + [input_opt
])
372 except DistutilsExecError
, msg
:
373 raise CompileError
, msg
375 elif ext
in self
._mc
_extensions
:
377 # Compile .MC to .RC file to .RES file.
378 # * '-h dir' specifies the directory for the
379 # generated include file
380 # * '-r dir' specifies the target directory of the
381 # generated RC file and the binary message resource
384 # For now (since there are no options to change this),
385 # we use the source-directory for the include file and
386 # the build directory for the RC file and message
387 # resources. This works at least for win32all.
389 h_dir
= os
.path
.dirname (src
)
390 rc_dir
= os
.path
.dirname (obj
)
392 # first compile .MC to .RC and .H file
393 self
.spawn ([self
.mc
] +
394 ['-h', h_dir
, '-r', rc_dir
] + [src
])
395 base
, _
= os
.path
.splitext (os
.path
.basename (src
))
396 rc_file
= os
.path
.join (rc_dir
, base
+ '.rc')
397 # then compile .RC to .RES file
398 self
.spawn ([self
.rc
] +
399 ["/fo" + obj
] + [rc_file
])
401 except DistutilsExecError
, msg
:
402 raise CompileError
, msg
405 # how to handle this file?
407 "Don't know how to compile %s to %s" % \
410 output_opt
= "/Fo" + obj
412 self
.spawn ([self
.cc
] + compile_opts
+ pp_opts
+
413 [input_opt
, output_opt
] +
415 except DistutilsExecError
, msg
:
416 raise CompileError
, msg
423 def create_static_lib (self
,
430 if not self
.initialized
: self
.initialize()
431 (objects
, output_dir
) = self
._fix
_object
_args
(objects
, output_dir
)
433 self
.library_filename (output_libname
, output_dir
=output_dir
)
435 if self
._need
_link
(objects
, output_filename
):
436 lib_args
= objects
+ ['/OUT:' + output_filename
]
438 pass # XXX what goes here?
440 self
.spawn ([self
.lib
] + lib_args
)
441 except DistutilsExecError
, msg
:
445 log
.debug("skipping %s (up-to-date)", output_filename
)
447 # create_static_lib ()
456 runtime_library_dirs
=None,
464 if not self
.initialized
: self
.initialize()
465 (objects
, output_dir
) = self
._fix
_object
_args
(objects
, output_dir
)
466 (libraries
, library_dirs
, runtime_library_dirs
) = \
467 self
._fix
_lib
_args
(libraries
, library_dirs
, runtime_library_dirs
)
469 if runtime_library_dirs
:
470 self
.warn ("I don't know what to do with 'runtime_library_dirs': "
471 + str (runtime_library_dirs
))
473 lib_opts
= gen_lib_options (self
,
474 library_dirs
, runtime_library_dirs
,
476 if output_dir
is not None:
477 output_filename
= os
.path
.join (output_dir
, output_filename
)
479 if self
._need
_link
(objects
, output_filename
):
481 if target_desc
== CCompiler
.EXECUTABLE
:
483 ldflags
= self
.ldflags_shared_debug
[1:]
485 ldflags
= self
.ldflags_shared
[1:]
488 ldflags
= self
.ldflags_shared_debug
490 ldflags
= self
.ldflags_shared
493 for sym
in (export_symbols
or []):
494 export_opts
.append("/EXPORT:" + sym
)
496 ld_args
= (ldflags
+ lib_opts
+ export_opts
+
497 objects
+ ['/OUT:' + output_filename
])
499 # The MSVC linker generates .lib and .exp files, which cannot be
500 # suppressed by any linker switches. The .lib files may even be
501 # needed! Make sure they are generated in the temporary build
502 # directory. Since they have different names for debug and release
503 # builds, they can go into the same directory.
504 if export_symbols
is not None:
505 (dll_name
, dll_ext
) = os
.path
.splitext(
506 os
.path
.basename(output_filename
))
507 implib_file
= os
.path
.join(
508 os
.path
.dirname(objects
[0]),
509 self
.library_filename(dll_name
))
510 ld_args
.append ('/IMPLIB:' + implib_file
)
513 ld_args
[:0] = extra_preargs
515 ld_args
.extend(extra_postargs
)
517 self
.mkpath (os
.path
.dirname (output_filename
))
519 self
.spawn ([self
.linker
] + ld_args
)
520 except DistutilsExecError
, msg
:
524 log
.debug("skipping %s (up-to-date)", output_filename
)
529 # -- Miscellaneous methods -----------------------------------------
530 # These are all used by the 'gen_lib_options() function, in
533 def library_dir_option (self
, dir):
534 return "/LIBPATH:" + dir
536 def runtime_library_dir_option (self
, dir):
537 raise DistutilsPlatformError
, \
538 "don't know how to set runtime library search path for MSVC++"
540 def library_option (self
, lib
):
541 return self
.library_filename (lib
)
544 def find_library_file (self
, dirs
, lib
, debug
=0):
545 # Prefer a debugging library if found (and requested), but deal
546 # with it if we don't have one.
548 try_names
= [lib
+ "_d", lib
]
552 for name
in try_names
:
553 libfile
= os
.path
.join(dir, self
.library_filename (name
))
554 if os
.path
.exists(libfile
):
557 # Oops, didn't find it in *any* of 'dirs'
560 # find_library_file ()
562 # Helper methods for using the MSVC registry settings
564 def find_exe(self
, exe
):
565 """Return path to an MSVC executable program.
567 Tries to find the program in several places: first, one of the
568 MSVC program search paths from the registry; next, the directories
569 in the PATH environment variable. If any of those work, return an
570 absolute path that is known to exist. If none of them work, just
571 return the original program name, 'exe'.
574 for p
in self
.__paths
:
575 fn
= os
.path
.join(os
.path
.abspath(p
), exe
)
576 if os
.path
.isfile(fn
):
579 # didn't find it; try existing path
580 for p
in string
.split(os
.environ
['Path'],';'):
581 fn
= os
.path
.join(os
.path
.abspath(p
),exe
)
582 if os
.path
.isfile(fn
):
587 def get_msvc_paths(self
, path
, platform
='x86'):
588 """Get a list of devstudio directories (include, lib or path).
590 Return a list of strings. The list will be empty if unable to
591 access the registry or appropriate registry keys not found.
594 if not _can_read_reg
:
597 path
= path
+ " dirs"
598 if self
.__version
>= 7:
599 key
= (r
"%s\%0.1f\VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories"
600 % (self
.__root
, self
.__version
))
602 key
= (r
"%s\6.0\Build System\Components\Platforms"
603 r
"\Win32 (%s)\Directories" % (self
.__root
, platform
))
606 d
= read_values(base
, key
)
608 if self
.__version
>= 7:
609 return string
.split(self
.__macros
.sub(d
[path
]), ";")
611 return string
.split(d
[path
], ";")
612 # MSVC 6 seems to create the registry entries we need only when
614 if self
.__version
== 6:
616 if read_values(base
, r
"%s\6.0" % self
.__root
) is not None:
617 self
.warn("It seems you have Visual Studio 6 installed, "
618 "but the expected registry settings are not present.\n"
619 "You must at least run the Visual Studio GUI once "
620 "so that these entries are created.")
624 def set_path_env_var(self
, name
):
625 """Set environment variable 'name' to an MSVC path type value.
627 This is equivalent to a SET command prior to execution of spawned
632 p
= self
.get_msvc_paths("library")
634 p
= self
.get_msvc_paths(name
)
636 os
.environ
[name
] = string
.join(p
, ';')