]>
git.saurik.com Git - wxWidgets.git/blob - wxPython/distutils/msvccompiler.py
1 """distutils.msvccompiler
3 Contains MSVCCompiler, an implementation of the abstract CCompiler class
4 for the Microsoft Visual Studio."""
6 # Written by Perry Stoll
7 # hacked by Robin Becker and Thomas Heller to do a better job of
8 # finding DevStudio (through the registry)
10 # This module should be kept compatible with Python 1.5.2.
14 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
51 HKEY_CLASSES_ROOT
= hkey_mod
.HKEY_CLASSES_ROOT
52 HKEY_LOCAL_MACHINE
= hkey_mod
.HKEY_LOCAL_MACHINE
53 HKEY_CURRENT_USER
= hkey_mod
.HKEY_CURRENT_USER
54 HKEY_USERS
= hkey_mod
.HKEY_USERS
58 def get_devstudio_versions ():
59 """Get list of devstudio versions from the Windows registry. Return a
60 list of strings containing version numbers; the list will be
61 empty if we were unable to access the registry (eg. couldn't import
62 a registry-access module) or the appropriate registry keys weren't
68 K
= 'Software\\Microsoft\\Devstudio'
70 for base
in (HKEY_CLASSES_ROOT
,
75 k
= RegOpenKeyEx(base
,K
)
80 if p
[0] in '123456789' and p
not in L
:
91 # get_devstudio_versions ()
94 def get_msvc_paths (path
, version
='6.0', platform
='x86'):
95 """Get a list of devstudio directories (include, lib or path). Return
96 a list of strings; will be empty list if unable to access the
97 registry or appropriate registry keys not found."""
105 path
= string
.upper(path
+ ' Dirs')
106 K
= ('Software\\Microsoft\\Devstudio\\%s\\' +
107 'Build System\\Components\\Platforms\\Win32 (%s)\\Directories') % \
109 for base
in (HKEY_CLASSES_ROOT
,
114 k
= RegOpenKeyEx(base
,K
)
118 (p
,v
,t
) = RegEnumValue(k
,i
)
119 if string
.upper(p
) == path
:
120 V
= string
.split(v
,';')
122 if hasattr(v
, "encode"):
127 if v
== '' or v
in L
: continue
140 def find_exe (exe
, version_number
):
141 """Try to find an MSVC executable program 'exe' (from version
142 'version_number' of MSVC) in several places: first, one of the MSVC
143 program search paths from the registry; next, the directories in the
144 PATH environment variable. If any of those work, return an absolute
145 path that is known to exist. If none of them work, just return the
146 original program name, 'exe'."""
148 for p
in get_msvc_paths ('path', version_number
):
149 fn
= os
.path
.join (os
.path
.abspath(p
), exe
)
150 if os
.path
.isfile(fn
):
153 # didn't find it; try existing path
154 for p
in string
.split (os
.environ
['Path'],';'):
155 fn
= os
.path
.join(os
.path
.abspath(p
),exe
)
156 if os
.path
.isfile(fn
):
159 return exe
# last desperate hope
162 def set_path_env_var (name
, version_number
):
163 """Set environment variable 'name' to an MSVC path type value obtained
164 from 'get_msvc_paths()'. This is equivalent to a SET command prior
165 to execution of spawned commands."""
167 p
= get_msvc_paths (name
, version_number
)
169 os
.environ
[name
] = string
.join (p
,';')
172 class MSVCCompiler (CCompiler
) :
173 """Concrete class that implements an interface to Microsoft Visual C++,
174 as defined by the CCompiler abstract class."""
176 compiler_type
= 'msvc'
178 # Just set this so CCompiler's constructor doesn't barf. We currently
179 # don't use the 'set_executables()' bureaucracy provided by CCompiler,
180 # as it really isn't necessary for this sort of single-compiler class.
181 # Would be nice to have a consistent interface with UnixCCompiler,
182 # though, so it's worth thinking about.
185 # Private class data (need to distinguish C from C++ source for compiler)
186 _c_extensions
= ['.c']
187 _cpp_extensions
= ['.cc', '.cpp', '.cxx']
188 _rc_extensions
= ['.rc']
189 _mc_extensions
= ['.mc']
191 # Needed for the filename generation methods provided by the
192 # base class, CCompiler.
193 src_extensions
= (_c_extensions
+ _cpp_extensions
+
194 _rc_extensions
+ _mc_extensions
)
195 res_extension
= '.res'
196 obj_extension
= '.obj'
197 static_lib_extension
= '.lib'
198 shared_lib_extension
= '.dll'
199 static_lib_format
= shared_lib_format
= '%s%s'
200 exe_extension
= '.exe'
208 CCompiler
.__init
__ (self
, verbose
, dry_run
, force
)
209 versions
= get_devstudio_versions ()
212 version
= versions
[0] # highest version
214 self
.cc
= find_exe("cl.exe", version
)
215 self
.linker
= find_exe("link.exe", version
)
216 self
.lib
= find_exe("lib.exe", version
)
217 self
.rc
= find_exe("rc.exe", version
) # resource compiler
218 self
.mc
= find_exe("mc.exe", version
) # message compiler
219 set_path_env_var ('lib', version
)
220 set_path_env_var ('include', version
)
221 path
=get_msvc_paths('path', version
)
223 for p
in string
.split(os
.environ
['path'],';'):
227 os
.environ
['path'] = string
.join(path
,';')
229 # devstudio not found in the registry
231 self
.linker
= "link.exe"
236 self
.preprocess_options
= None
237 self
.compile_options
= [ '/nologo', '/Ox', '/MD', '/W3', '/GX' ,
239 self
.compile_options_debug
= ['/nologo', '/Od', '/MDd', '/W3', '/GX',
242 self
.ldflags_shared
= ['/DLL', '/nologo', '/INCREMENTAL:NO']
243 self
.ldflags_shared_debug
= [
244 '/DLL', '/nologo', '/INCREMENTAL:no', '/pdb:None', '/DEBUG'
246 self
.ldflags_static
= [ '/nologo']
249 # -- Worker methods ------------------------------------------------
251 def object_filenames (self
,
255 # Copied from ccompiler.py, extended to return .res as 'object'-file
257 if output_dir
is None: output_dir
= ''
259 for src_name
in source_filenames
:
260 (base
, ext
) = os
.path
.splitext (src_name
)
261 if ext
not in self
.src_extensions
:
262 # Better to raise an exception instead of silently continuing
263 # and later complain about sources and targets having
265 raise CompileError ("Don't know how to compile %s" % src_name
)
267 base
= os
.path
.basename (base
)
268 if ext
in self
._rc
_extensions
:
269 obj_names
.append (os
.path
.join (output_dir
,
270 base
+ self
.res_extension
))
271 elif ext
in self
._mc
_extensions
:
272 obj_names
.append (os
.path
.join (output_dir
,
273 base
+ self
.res_extension
))
275 obj_names
.append (os
.path
.join (output_dir
,
276 base
+ self
.obj_extension
))
279 # object_filenames ()
282 def compile(self
, sources
,
283 output_dir
=None, macros
=None, include_dirs
=None, debug
=0,
284 extra_preargs
=None, extra_postargs
=None, depends
=None):
286 macros
, objects
, extra_postargs
, pp_opts
, build
= \
287 self
._setup
_compile
(output_dir
, macros
, include_dirs
, sources
,
288 depends
, extra_postargs
)
290 compile_opts
= extra_preargs
or []
291 compile_opts
.append ('/c')
293 compile_opts
.extend(self
.compile_options_debug
)
295 compile_opts
.extend(self
.compile_options
)
297 for obj
, (src
, ext
) in build
.items():
299 # pass the full pathname to MSVC in debug mode,
300 # this allows the debugger to find the source file
301 # without asking the user to browse for it
302 src
= os
.path
.abspath(src
)
304 if ext
in self
._c
_extensions
:
305 input_opt
= "/Tc" + src
306 elif ext
in self
._cpp
_extensions
:
307 input_opt
= "/Tp" + src
308 elif ext
in self
._rc
_extensions
:
309 # compile .RC to .RES file
311 output_opt
= "/fo" + obj
313 self
.spawn ([self
.rc
] + pp_opts
+
314 [output_opt
] + [input_opt
])
315 except DistutilsExecError
, msg
:
316 raise CompileError
, msg
318 elif ext
in self
._mc
_extensions
:
320 # Compile .MC to .RC file to .RES file.
321 # * '-h dir' specifies the directory for the
322 # generated include file
323 # * '-r dir' specifies the target directory of the
324 # generated RC file and the binary message resource
327 # For now (since there are no options to change this),
328 # we use the source-directory for the include file and
329 # the build directory for the RC file and message
330 # resources. This works at least for win32all.
332 h_dir
= os
.path
.dirname (src
)
333 rc_dir
= os
.path
.dirname (obj
)
335 # first compile .MC to .RC and .H file
336 self
.spawn ([self
.mc
] +
337 ['-h', h_dir
, '-r', rc_dir
] + [src
])
338 base
, _
= os
.path
.splitext (os
.path
.basename (src
))
339 rc_file
= os
.path
.join (rc_dir
, base
+ '.rc')
340 # then compile .RC to .RES file
341 self
.spawn ([self
.rc
] +
342 ["/fo" + obj
] + [rc_file
])
344 except DistutilsExecError
, msg
:
345 raise CompileError
, msg
348 # how to handle this file?
350 "Don't know how to compile %s to %s" % \
353 output_opt
= "/Fo" + obj
355 self
.spawn ([self
.cc
] + compile_opts
+ pp_opts
+
356 [input_opt
, output_opt
] +
358 except DistutilsExecError
, msg
:
359 raise CompileError
, msg
366 def create_static_lib (self
,
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 self
.spawn ([self
.lib
] + lib_args
)
383 except DistutilsExecError
, msg
:
387 log
.debug("skipping %s (up-to-date)", output_filename
)
389 # create_static_lib ()
398 runtime_library_dirs
=None,
406 (objects
, output_dir
) = self
._fix
_object
_args
(objects
, output_dir
)
407 (libraries
, library_dirs
, runtime_library_dirs
) = \
408 self
._fix
_lib
_args
(libraries
, library_dirs
, runtime_library_dirs
)
410 if runtime_library_dirs
:
411 self
.warn ("I don't know what to do with 'runtime_library_dirs': "
412 + str (runtime_library_dirs
))
414 lib_opts
= gen_lib_options (self
,
415 library_dirs
, runtime_library_dirs
,
417 if output_dir
is not None:
418 output_filename
= os
.path
.join (output_dir
, output_filename
)
420 if self
._need
_link
(objects
, output_filename
):
422 if target_desc
== CCompiler
.EXECUTABLE
:
424 ldflags
= self
.ldflags_shared_debug
[1:]
426 ldflags
= self
.ldflags_shared
[1:]
429 ldflags
= self
.ldflags_shared_debug
431 ldflags
= self
.ldflags_shared
434 for sym
in (export_symbols
or []):
435 export_opts
.append("/EXPORT:" + sym
)
437 ld_args
= (ldflags
+ lib_opts
+ export_opts
+
438 objects
+ ['/OUT:' + output_filename
])
440 # The MSVC linker generates .lib and .exp files, which cannot be
441 # suppressed by any linker switches. The .lib files may even be
442 # needed! Make sure they are generated in the temporary build
443 # directory. Since they have different names for debug and release
444 # builds, they can go into the same directory.
445 if export_symbols
is not None:
446 (dll_name
, dll_ext
) = os
.path
.splitext(
447 os
.path
.basename(output_filename
))
448 implib_file
= os
.path
.join(
449 os
.path
.dirname(objects
[0]),
450 self
.library_filename(dll_name
))
451 ld_args
.append ('/IMPLIB:' + implib_file
)
454 ld_args
[:0] = extra_preargs
456 ld_args
.extend(extra_postargs
)
458 self
.mkpath (os
.path
.dirname (output_filename
))
460 self
.spawn ([self
.linker
] + ld_args
)
461 except DistutilsExecError
, msg
:
465 log
.debug("skipping %s (up-to-date)", output_filename
)
470 # -- Miscellaneous methods -----------------------------------------
471 # These are all used by the 'gen_lib_options() function, in
474 def library_dir_option (self
, dir):
475 return "/LIBPATH:" + dir
477 def runtime_library_dir_option (self
, dir):
478 raise DistutilsPlatformError
, \
479 "don't know how to set runtime library search path for MSVC++"
481 def library_option (self
, lib
):
482 return self
.library_filename (lib
)
485 def find_library_file (self
, dirs
, lib
, debug
=0):
486 # Prefer a debugging library if found (and requested), but deal
487 # with it if we don't have one.
489 try_names
= [lib
+ "_d", lib
]
493 for name
in try_names
:
494 libfile
= os
.path
.join(dir, self
.library_filename (name
))
495 if os
.path
.exists(libfile
):
498 # Oops, didn't find it in *any* of 'dirs'
501 # find_library_file ()