]>
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.
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 1.5.2.
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")
128 self
.set_macro("FrameworkSDKDir", net
, "sdkinstallrootv1.1")
130 self
.set_macro("FrameworkSDKDir", net
, "sdkinstallroot")
132 p
= r
"Software\Microsoft\NET Framework Setup\Product"
135 h
= RegOpenKeyEx(base
, p
)
138 key
= RegEnumKey(h
, 0)
139 d
= read_values(base
, r
"%s\%s" % (p
, key
))
140 self
.macros
["$(FrameworkVersion)"] = d
["version"]
143 for k
, v
in self
.macros
.items():
144 s
= string
.replace(s
, k
, v
)
147 def get_build_version():
148 """Return the version of MSVC that was used to build Python.
150 For Python 2.3 and up, the version number is included in
151 sys.version. For earlier versions, assume the compiler is MSVC 6.
155 i
= string
.find(sys
.version
, prefix
)
159 s
, rest
= sys
.version
[i
:].split(" ", 1)
160 majorVersion
= int(s
[:-2]) - 6
161 minorVersion
= int(s
[2:3]) / 10.0
162 # I don't think paths are affected by minor version in version 6
163 if majorVersion
== 6:
165 if majorVersion
>= 6:
166 return majorVersion
+ minorVersion
167 # else we don't know what version of the compiler this is
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'
201 def __init__ (self
, verbose
=0, dry_run
=0, force
=0):
202 CCompiler
.__init
__ (self
, verbose
, dry_run
, force
)
203 self
.__version
= get_build_version()
204 if self
.__version
>= 7:
205 self
.__root
= r
"Software\Microsoft\VisualStudio"
206 self
.__macros
= MacroExpander(self
.__version
)
208 self
.__root
= r
"Software\Microsoft\Devstudio"
209 self
.__paths
= self
.get_msvc_paths("path")
211 if len (self
.__paths
) == 0:
212 raise DistutilsPlatformError
, \
213 ("Python was built with version %s of Visual Studio, "
214 "and extensions need to be built with the same "
215 "version of the compiler, but it isn't installed." % self
.__version
)
217 self
.cc
= self
.find_exe("cl.exe")
218 self
.linker
= self
.find_exe("link.exe")
219 self
.lib
= self
.find_exe("lib.exe")
220 self
.rc
= self
.find_exe("rc.exe") # resource compiler
221 self
.mc
= self
.find_exe("mc.exe") # message compiler
222 self
.set_path_env_var('lib')
223 self
.set_path_env_var('include')
225 # extend the MSVC path with the current path
227 for p
in string
.split(os
.environ
['path'], ';'):
228 self
.__paths
.append(p
)
231 os
.environ
['path'] = string
.join(self
.__paths
, ';')
233 self
.preprocess_options
= None
234 self
.compile_options
= [ '/nologo', '/Ox', '/MD', '/W3', '/GX' ,
236 self
.compile_options_debug
= ['/nologo', '/Od', '/MDd', '/W3', '/GX',
239 self
.ldflags_shared
= ['/DLL', '/nologo', '/INCREMENTAL:NO']
240 self
.ldflags_shared_debug
= [
241 '/DLL', '/nologo', '/INCREMENTAL:no', '/pdb:None', '/DEBUG'
243 self
.ldflags_static
= [ '/nologo']
246 # -- Worker methods ------------------------------------------------
248 def object_filenames (self
,
252 # Copied from ccompiler.py, extended to return .res as 'object'-file
254 if output_dir
is None: output_dir
= ''
256 for src_name
in source_filenames
:
257 (base
, ext
) = os
.path
.splitext (src_name
)
258 if ext
not in self
.src_extensions
:
259 # Better to raise an exception instead of silently continuing
260 # and later complain about sources and targets having
262 raise CompileError ("Don't know how to compile %s" % src_name
)
264 base
= os
.path
.basename (base
)
265 if ext
in self
._rc
_extensions
:
266 obj_names
.append (os
.path
.join (output_dir
,
267 base
+ self
.res_extension
))
268 elif ext
in self
._mc
_extensions
:
269 obj_names
.append (os
.path
.join (output_dir
,
270 base
+ self
.res_extension
))
272 obj_names
.append (os
.path
.join (output_dir
,
273 base
+ self
.obj_extension
))
276 # object_filenames ()
279 def compile(self
, sources
,
280 output_dir
=None, macros
=None, include_dirs
=None, debug
=0,
281 extra_preargs
=None, extra_postargs
=None, depends
=None):
283 macros
, objects
, extra_postargs
, pp_opts
, build
= \
284 self
._setup
_compile
(output_dir
, macros
, include_dirs
, sources
,
285 depends
, extra_postargs
)
287 compile_opts
= extra_preargs
or []
288 compile_opts
.append ('/c')
290 compile_opts
.extend(self
.compile_options_debug
)
292 compile_opts
.extend(self
.compile_options
)
294 for obj
, (src
, ext
) in build
.items():
296 # pass the full pathname to MSVC in debug mode,
297 # this allows the debugger to find the source file
298 # without asking the user to browse for it
299 src
= os
.path
.abspath(src
)
301 if ext
in self
._c
_extensions
:
302 input_opt
= "/Tc" + src
303 elif ext
in self
._cpp
_extensions
:
304 input_opt
= "/Tp" + src
305 elif ext
in self
._rc
_extensions
:
306 # compile .RC to .RES file
308 output_opt
= "/fo" + obj
310 self
.spawn ([self
.rc
] + pp_opts
+
311 [output_opt
] + [input_opt
])
312 except DistutilsExecError
, msg
:
313 raise CompileError
, msg
315 elif ext
in self
._mc
_extensions
:
317 # Compile .MC to .RC file to .RES file.
318 # * '-h dir' specifies the directory for the
319 # generated include file
320 # * '-r dir' specifies the target directory of the
321 # generated RC file and the binary message resource
324 # For now (since there are no options to change this),
325 # we use the source-directory for the include file and
326 # the build directory for the RC file and message
327 # resources. This works at least for win32all.
329 h_dir
= os
.path
.dirname (src
)
330 rc_dir
= os
.path
.dirname (obj
)
332 # first compile .MC to .RC and .H file
333 self
.spawn ([self
.mc
] +
334 ['-h', h_dir
, '-r', rc_dir
] + [src
])
335 base
, _
= os
.path
.splitext (os
.path
.basename (src
))
336 rc_file
= os
.path
.join (rc_dir
, base
+ '.rc')
337 # then compile .RC to .RES file
338 self
.spawn ([self
.rc
] +
339 ["/fo" + obj
] + [rc_file
])
341 except DistutilsExecError
, msg
:
342 raise CompileError
, msg
345 # how to handle this file?
347 "Don't know how to compile %s to %s" % \
350 output_opt
= "/Fo" + obj
352 self
.spawn ([self
.cc
] + compile_opts
+ pp_opts
+
353 [input_opt
, output_opt
] +
355 except DistutilsExecError
, msg
:
356 raise CompileError
, msg
363 def create_static_lib (self
,
370 (objects
, output_dir
) = self
._fix
_object
_args
(objects
, output_dir
)
372 self
.library_filename (output_libname
, output_dir
=output_dir
)
374 if self
._need
_link
(objects
, output_filename
):
375 lib_args
= objects
+ ['/OUT:' + output_filename
]
377 pass # XXX what goes here?
379 self
.spawn ([self
.lib
] + lib_args
)
380 except DistutilsExecError
, msg
:
384 log
.debug("skipping %s (up-to-date)", output_filename
)
386 # create_static_lib ()
395 runtime_library_dirs
=None,
403 (objects
, output_dir
) = self
._fix
_object
_args
(objects
, output_dir
)
404 (libraries
, library_dirs
, runtime_library_dirs
) = \
405 self
._fix
_lib
_args
(libraries
, library_dirs
, runtime_library_dirs
)
407 if runtime_library_dirs
:
408 self
.warn ("I don't know what to do with 'runtime_library_dirs': "
409 + str (runtime_library_dirs
))
411 lib_opts
= gen_lib_options (self
,
412 library_dirs
, runtime_library_dirs
,
414 if output_dir
is not None:
415 output_filename
= os
.path
.join (output_dir
, output_filename
)
417 if self
._need
_link
(objects
, output_filename
):
419 if target_desc
== CCompiler
.EXECUTABLE
:
421 ldflags
= self
.ldflags_shared_debug
[1:]
423 ldflags
= self
.ldflags_shared
[1:]
426 ldflags
= self
.ldflags_shared_debug
428 ldflags
= self
.ldflags_shared
431 for sym
in (export_symbols
or []):
432 export_opts
.append("/EXPORT:" + sym
)
434 ld_args
= (ldflags
+ lib_opts
+ export_opts
+
435 objects
+ ['/OUT:' + output_filename
])
437 # The MSVC linker generates .lib and .exp files, which cannot be
438 # suppressed by any linker switches. The .lib files may even be
439 # needed! Make sure they are generated in the temporary build
440 # directory. Since they have different names for debug and release
441 # builds, they can go into the same directory.
442 if export_symbols
is not None:
443 (dll_name
, dll_ext
) = os
.path
.splitext(
444 os
.path
.basename(output_filename
))
445 implib_file
= os
.path
.join(
446 os
.path
.dirname(objects
[0]),
447 self
.library_filename(dll_name
))
448 ld_args
.append ('/IMPLIB:' + implib_file
)
451 ld_args
[:0] = extra_preargs
453 ld_args
.extend(extra_postargs
)
455 self
.mkpath (os
.path
.dirname (output_filename
))
457 self
.spawn ([self
.linker
] + ld_args
)
458 except DistutilsExecError
, msg
:
462 log
.debug("skipping %s (up-to-date)", output_filename
)
467 # -- Miscellaneous methods -----------------------------------------
468 # These are all used by the 'gen_lib_options() function, in
471 def library_dir_option (self
, dir):
472 return "/LIBPATH:" + dir
474 def runtime_library_dir_option (self
, dir):
475 raise DistutilsPlatformError
, \
476 "don't know how to set runtime library search path for MSVC++"
478 def library_option (self
, lib
):
479 return self
.library_filename (lib
)
482 def find_library_file (self
, dirs
, lib
, debug
=0):
483 # Prefer a debugging library if found (and requested), but deal
484 # with it if we don't have one.
486 try_names
= [lib
+ "_d", lib
]
490 for name
in try_names
:
491 libfile
= os
.path
.join(dir, self
.library_filename (name
))
492 if os
.path
.exists(libfile
):
495 # Oops, didn't find it in *any* of 'dirs'
498 # find_library_file ()
500 # Helper methods for using the MSVC registry settings
502 def find_exe(self
, exe
):
503 """Return path to an MSVC executable program.
505 Tries to find the program in several places: first, one of the
506 MSVC program search paths from the registry; next, the directories
507 in the PATH environment variable. If any of those work, return an
508 absolute path that is known to exist. If none of them work, just
509 return the original program name, 'exe'.
512 for p
in self
.__paths
:
513 fn
= os
.path
.join(os
.path
.abspath(p
), exe
)
514 if os
.path
.isfile(fn
):
517 # didn't find it; try existing path
518 for p
in string
.split(os
.environ
['Path'],';'):
519 fn
= os
.path
.join(os
.path
.abspath(p
),exe
)
520 if os
.path
.isfile(fn
):
525 def get_msvc_paths(self
, path
, platform
='x86'):
526 """Get a list of devstudio directories (include, lib or path).
528 Return a list of strings. The list will be empty if unable to
529 access the registry or appropriate registry keys not found.
532 if not _can_read_reg
:
535 path
= path
+ " dirs"
536 if self
.__version
>= 7:
537 key
= (r
"%s\%0.1f\VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories"
538 % (self
.__root
, self
.__version
))
540 key
= (r
"%s\6.0\Build System\Components\Platforms"
541 r
"\Win32 (%s)\Directories" % (self
.__root
, platform
))
544 d
= read_values(base
, key
)
546 if self
.__version
>= 7:
547 return string
.split(self
.__macros
.sub(d
[path
]), ";")
549 return string
.split(d
[path
], ";")
552 def set_path_env_var(self
, name
):
553 """Set environment variable 'name' to an MSVC path type value.
555 This is equivalent to a SET command prior to execution of spawned
560 p
= self
.get_msvc_paths("library")
562 p
= self
.get_msvc_paths(name
)
564 os
.environ
[name
] = string
.join(p
, ';')