1 """distutils.msvccompiler
3 Contains MSVCCompiler, an implementation of the abstract CCompiler class
4 for the Microsoft Visual Studio."""
7 # created 1999/08/19, 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
15 from distutils
.errors
import \
16 DistutilsExecError
, DistutilsPlatformError
, \
17 CompileError
, LibError
, LinkError
18 from distutils
.ccompiler
import \
19 CCompiler
, gen_preprocess_options
, 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
50 HKEY_CLASSES_ROOT
= hkey_mod
.HKEY_CLASSES_ROOT
51 HKEY_LOCAL_MACHINE
= hkey_mod
.HKEY_LOCAL_MACHINE
52 HKEY_CURRENT_USER
= hkey_mod
.HKEY_CURRENT_USER
53 HKEY_USERS
= hkey_mod
.HKEY_USERS
57 def get_devstudio_versions ():
58 """Get list of devstudio versions from the Windows registry. Return a
59 list of strings containing version numbers; the list will be
60 empty if we were unable to access the registry (eg. couldn't import
61 a registry-access module) or the appropriate registry keys weren't
67 K
= 'Software\\Microsoft\\Devstudio'
69 for base
in (HKEY_CLASSES_ROOT
,
74 k
= RegOpenKeyEx(base
,K
)
79 if p
[0] in '123456789' and p
not in L
:
90 # get_devstudio_versions ()
93 def get_msvc_paths (path
, version
='6.0', platform
='x86'):
94 """Get a list of devstudio directories (include, lib or path). Return
95 a list of strings; will be empty list if unable to access the
96 registry or appropriate registry keys not found."""
104 path
= string
.upper(path
+ ' Dirs')
105 K
= ('Software\\Microsoft\\Devstudio\\%s\\' +
106 'Build System\\Components\\Platforms\\Win32 (%s)\\Directories') % \
108 for base
in (HKEY_CLASSES_ROOT
,
113 k
= RegOpenKeyEx(base
,K
)
117 (p
,v
,t
) = RegEnumValue(k
,i
)
118 if string
.upper(p
) == path
:
119 V
= string
.split(v
,';')
121 if hasattr(v
, "encode"):
126 if v
== '' or v
in L
: continue
139 def find_exe (exe
, version_number
):
140 """Try to find an MSVC executable program 'exe' (from version
141 'version_number' of MSVC) in several places: first, one of the MSVC
142 program search paths from the registry; next, the directories in the
143 PATH environment variable. If any of those work, return an absolute
144 path that is known to exist. If none of them work, just return the
145 original program name, 'exe'."""
147 for p
in get_msvc_paths ('path', version_number
):
148 fn
= os
.path
.join (os
.path
.abspath(p
), exe
)
149 if os
.path
.isfile(fn
):
152 # didn't find it; try existing path
153 for p
in string
.split (os
.environ
['Path'],';'):
154 fn
= os
.path
.join(os
.path
.abspath(p
),exe
)
155 if os
.path
.isfile(fn
):
158 return exe
# last desperate hope
161 def set_path_env_var (name
, version_number
):
162 """Set environment variable 'name' to an MSVC path type value obtained
163 from 'get_msvc_paths()'. This is equivalent to a SET command prior
164 to execution of spawned commands."""
166 p
= get_msvc_paths (name
, version_number
)
168 os
.environ
[name
] = string
.join (p
,';')
171 class MSVCCompiler (CCompiler
) :
172 """Concrete class that implements an interface to Microsoft Visual C++,
173 as defined by the CCompiler abstract class."""
175 compiler_type
= 'msvc'
177 # Just set this so CCompiler's constructor doesn't barf. We currently
178 # don't use the 'set_executables()' bureaucracy provided by CCompiler,
179 # as it really isn't necessary for this sort of single-compiler class.
180 # Would be nice to have a consistent interface with UnixCCompiler,
181 # though, so it's worth thinking about.
184 # Private class data (need to distinguish C from C++ source for compiler)
185 _c_extensions
= ['.c']
186 _cpp_extensions
= ['.cc', '.cpp', '.cxx']
187 _rc_extensions
= ['.rc']
188 _mc_extensions
= ['.mc']
190 # Needed for the filename generation methods provided by the
191 # base class, CCompiler.
192 src_extensions
= (_c_extensions
+ _cpp_extensions
+
193 _rc_extensions
+ _mc_extensions
)
194 res_extension
= '.res'
195 obj_extension
= '.obj'
196 static_lib_extension
= '.lib'
197 shared_lib_extension
= '.dll'
198 static_lib_format
= shared_lib_format
= '%s%s'
199 exe_extension
= '.exe'
207 CCompiler
.__init
__ (self
, verbose
, dry_run
, force
)
208 versions
= get_devstudio_versions ()
211 version
= versions
[0] # highest version
213 self
.cc
= find_exe("cl.exe", version
)
214 self
.linker
= find_exe("link.exe", version
)
215 self
.lib
= find_exe("lib.exe", version
)
216 self
.rc
= find_exe("rc.exe", version
) # resource compiler
217 self
.mc
= find_exe("mc.exe", version
) # message compiler
218 set_path_env_var ('lib', version
)
219 set_path_env_var ('include', version
)
220 path
=get_msvc_paths('path', version
)
222 for p
in string
.split(os
.environ
['path'],';'):
226 os
.environ
['path'] = string
.join(path
,';')
228 # devstudio not found in the registry
230 self
.linker
= "link.exe"
235 self
.preprocess_options
= None
236 self
.compile_options
= [ '/nologo', '/Ox', '/MD', '/W3', '/GX' ,
238 self
.compile_options_debug
= ['/nologo', '/Od', '/MDd', '/W3', '/GX',
241 self
.ldflags_shared
= ['/DLL', '/nologo', '/INCREMENTAL:NO']
242 self
.ldflags_shared_debug
= [
243 '/DLL', '/nologo', '/INCREMENTAL:no', '/pdb:None', '/DEBUG'
245 self
.ldflags_static
= [ '/nologo']
248 # -- Worker methods ------------------------------------------------
250 def object_filenames (self
,
254 # Copied from ccompiler.py, extended to return .res as 'object'-file
256 if output_dir
is None: output_dir
= ''
258 for src_name
in source_filenames
:
259 (base
, ext
) = os
.path
.splitext (src_name
)
260 if ext
not in self
.src_extensions
:
261 # Better to raise an exception instead of silently continuing
262 # and later complain about sources and targets having
264 raise CompileError ("Don't know how to compile %s" % src_name
)
266 base
= os
.path
.basename (base
)
267 if ext
in self
._rc
_extensions
:
268 obj_names
.append (os
.path
.join (output_dir
,
269 base
+ self
.res_extension
))
270 elif ext
in self
._mc
_extensions
:
271 obj_names
.append (os
.path
.join (output_dir
,
272 base
+ self
.res_extension
))
274 obj_names
.append (os
.path
.join (output_dir
,
275 base
+ self
.obj_extension
))
278 # object_filenames ()
281 def compile(self
, sources
,
282 output_dir
=None, macros
=None, include_dirs
=None, debug
=0,
283 extra_preargs
=None, extra_postargs
=None, depends
=None):
285 macros
, objects
, extra_postargs
, pp_opts
, build
= \
286 self
._setup
_compile
(output_dir
, macros
, include_dirs
, sources
,
287 depends
, extra_postargs
)
289 compile_opts
= extra_preargs
or []
290 compile_opts
.append ('/c')
292 compile_opts
.extend(self
.compile_options_debug
)
294 compile_opts
.extend(self
.compile_options
)
296 for obj
, (src
, ext
) in build
.items():
298 # pass the full pathname to MSVC in debug mode,
299 # this allows the debugger to find the source file
300 # without asking the user to browse for it
301 src
= os
.path
.abspath(src
)
303 if ext
in self
._c
_extensions
:
304 input_opt
= "/Tc" + src
305 elif ext
in self
._cpp
_extensions
:
306 input_opt
= "/Tp" + src
307 elif ext
in self
._rc
_extensions
:
308 # compile .RC to .RES file
310 output_opt
= "/fo" + obj
312 self
.spawn ([self
.rc
] +
313 [output_opt
] + [input_opt
])
314 except DistutilsExecError
, msg
:
315 raise CompileError
, msg
317 elif ext
in self
._mc
_extensions
:
319 # Compile .MC to .RC file to .RES file.
320 # * '-h dir' specifies the directory for the
321 # generated include file
322 # * '-r dir' specifies the target directory of the
323 # generated RC file and the binary message resource
326 # For now (since there are no options to change this),
327 # we use the source-directory for the include file and
328 # the build directory for the RC file and message
329 # resources. This works at least for win32all.
331 h_dir
= os
.path
.dirname (src
)
332 rc_dir
= os
.path
.dirname (obj
)
334 # first compile .MC to .RC and .H file
335 self
.spawn ([self
.mc
] +
336 ['-h', h_dir
, '-r', rc_dir
] + [src
])
337 base
, _
= os
.path
.splitext (os
.path
.basename (src
))
338 rc_file
= os
.path
.join (rc_dir
, base
+ '.rc')
339 # then compile .RC to .RES file
340 self
.spawn ([self
.rc
] +
341 ["/fo" + obj
] + [rc_file
])
343 except DistutilsExecError
, msg
:
344 raise CompileError
, msg
347 # how to handle this file?
349 "Don't know how to compile %s to %s" % \
352 output_opt
= "/Fo" + obj
354 self
.spawn ([self
.cc
] + compile_opts
+ pp_opts
+
355 [input_opt
, output_opt
] +
357 except DistutilsExecError
, msg
:
358 raise CompileError
, msg
365 def create_static_lib (self
,
371 extra_postargs
=None):
373 (objects
, output_dir
) = self
._fix
_object
_args
(objects
, output_dir
)
375 self
.library_filename (output_libname
, output_dir
=output_dir
)
377 if self
._need
_link
(objects
, output_filename
):
378 lib_args
= objects
+ ['/OUT:' + output_filename
]
380 pass # XXX what goes here?
382 lib_args
[:0] = extra_preargs
384 lib_args
.extend (extra_postargs
)
386 self
.spawn ([self
.lib
] + lib_args
)
387 except DistutilsExecError
, msg
:
391 log
.debug("skipping %s (up-to-date)", output_filename
)
393 # create_static_lib ()
402 runtime_library_dirs
=None,
409 (objects
, output_dir
) = self
._fix
_object
_args
(objects
, output_dir
)
410 (libraries
, library_dirs
, runtime_library_dirs
) = \
411 self
._fix
_lib
_args
(libraries
, library_dirs
, runtime_library_dirs
)
413 if runtime_library_dirs
:
414 self
.warn ("I don't know what to do with 'runtime_library_dirs': "
415 + str (runtime_library_dirs
))
417 lib_opts
= gen_lib_options (self
,
418 library_dirs
, runtime_library_dirs
,
420 if output_dir
is not None:
421 output_filename
= os
.path
.join (output_dir
, output_filename
)
423 if self
._need
_link
(objects
, output_filename
):
425 if target_desc
== CCompiler
.EXECUTABLE
:
427 ldflags
= self
.ldflags_shared_debug
[1:]
429 ldflags
= self
.ldflags_shared
[1:]
432 ldflags
= self
.ldflags_shared_debug
434 ldflags
= self
.ldflags_shared
437 for sym
in (export_symbols
or []):
438 export_opts
.append("/EXPORT:" + sym
)
440 ld_args
= (ldflags
+ lib_opts
+ export_opts
+
441 objects
+ ['/OUT:' + output_filename
])
443 # The MSVC linker generates .lib and .exp files, which cannot be
444 # suppressed by any linker switches. The .lib files may even be
445 # needed! Make sure they are generated in the temporary build
446 # directory. Since they have different names for debug and release
447 # builds, they can go into the same directory.
448 if export_symbols
is not None:
449 (dll_name
, dll_ext
) = os
.path
.splitext(
450 os
.path
.basename(output_filename
))
451 implib_file
= os
.path
.join(
452 os
.path
.dirname(objects
[0]),
453 self
.library_filename(dll_name
))
454 ld_args
.append ('/IMPLIB:' + implib_file
)
457 ld_args
[:0] = extra_preargs
459 ld_args
.extend(extra_postargs
)
461 self
.mkpath (os
.path
.dirname (output_filename
))
463 self
.spawn ([self
.linker
] + ld_args
)
464 except DistutilsExecError
, msg
:
468 log
.debug("skipping %s (up-to-date)", output_filename
)
473 # -- Miscellaneous methods -----------------------------------------
474 # These are all used by the 'gen_lib_options() function, in
477 def library_dir_option (self
, dir):
478 return "/LIBPATH:" + dir
480 def runtime_library_dir_option (self
, dir):
481 raise DistutilsPlatformError
, \
482 "don't know how to set runtime library search path for MSVC++"
484 def library_option (self
, lib
):
485 return self
.library_filename (lib
)
488 def find_library_file (self
, dirs
, lib
, debug
=0):
489 # Prefer a debugging library if found (and requested), but deal
490 # with it if we don't have one.
492 try_names
= [lib
+ "_d", lib
]
496 for name
in try_names
:
497 libfile
= os
.path
.join(dir, self
.library_filename (name
))
498 if os
.path
.exists(libfile
):
501 # Oops, didn't find it in *any* of 'dirs'
504 # find_library_file ()