]> git.saurik.com Git - wxWidgets.git/blob - wxPython/distutils/msvccompiler.py
applied an extended version of patch 685795: clean up view [de]activation
[wxWidgets.git] / wxPython / distutils / msvccompiler.py
1 """distutils.msvccompiler
2
3 Contains MSVCCompiler, an implementation of the abstract CCompiler class
4 for the Microsoft Visual Studio."""
5
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)
9
10 # This module should be kept compatible with Python 1.5.2.
11
12 __revision__ = "$Id$"
13
14 import sys, os, string
15 from types import *
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
22
23 _can_read_reg = 0
24 try:
25 import _winreg
26
27 _can_read_reg = 1
28 hkey_mod = _winreg
29
30 RegOpenKeyEx = _winreg.OpenKeyEx
31 RegEnumKey = _winreg.EnumKey
32 RegEnumValue = _winreg.EnumValue
33 RegError = _winreg.error
34
35 except ImportError:
36 try:
37 import win32api
38 import win32con
39 _can_read_reg = 1
40 hkey_mod = win32con
41
42 RegOpenKeyEx = win32api.RegOpenKeyEx
43 RegEnumKey = win32api.RegEnumKey
44 RegEnumValue = win32api.RegEnumValue
45 RegError = win32api.error
46
47 except ImportError:
48 pass
49
50 if _can_read_reg:
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
55
56
57
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
63 found."""
64
65 if not _can_read_reg:
66 return []
67
68 K = 'Software\\Microsoft\\Devstudio'
69 L = []
70 for base in (HKEY_CLASSES_ROOT,
71 HKEY_LOCAL_MACHINE,
72 HKEY_CURRENT_USER,
73 HKEY_USERS):
74 try:
75 k = RegOpenKeyEx(base,K)
76 i = 0
77 while 1:
78 try:
79 p = RegEnumKey(k,i)
80 if p[0] in '123456789' and p not in L:
81 L.append(p)
82 except RegError:
83 break
84 i = i + 1
85 except RegError:
86 pass
87 L.sort()
88 L.reverse()
89 return L
90
91 # get_devstudio_versions ()
92
93
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."""
98
99 if not _can_read_reg:
100 return []
101
102 L = []
103 if path=='lib':
104 path= 'Library'
105 path = string.upper(path + ' Dirs')
106 K = ('Software\\Microsoft\\Devstudio\\%s\\' +
107 'Build System\\Components\\Platforms\\Win32 (%s)\\Directories') % \
108 (version,platform)
109 for base in (HKEY_CLASSES_ROOT,
110 HKEY_LOCAL_MACHINE,
111 HKEY_CURRENT_USER,
112 HKEY_USERS):
113 try:
114 k = RegOpenKeyEx(base,K)
115 i = 0
116 while 1:
117 try:
118 (p,v,t) = RegEnumValue(k,i)
119 if string.upper(p) == path:
120 V = string.split(v,';')
121 for v in V:
122 if hasattr(v, "encode"):
123 try:
124 v = v.encode("mbcs")
125 except UnicodeError:
126 pass
127 if v == '' or v in L: continue
128 L.append(v)
129 break
130 i = i + 1
131 except RegError:
132 break
133 except RegError:
134 pass
135 return L
136
137 # get_msvc_paths()
138
139
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'."""
147
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):
151 return fn
152
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):
157 return fn
158
159 return exe # last desperate hope
160
161
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."""
166
167 p = get_msvc_paths (name, version_number)
168 if p:
169 os.environ[name] = string.join (p,';')
170
171
172 class MSVCCompiler (CCompiler) :
173 """Concrete class that implements an interface to Microsoft Visual C++,
174 as defined by the CCompiler abstract class."""
175
176 compiler_type = 'msvc'
177
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.
183 executables = {}
184
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']
190
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'
201
202
203 def __init__ (self,
204 verbose=0,
205 dry_run=0,
206 force=0):
207
208 CCompiler.__init__ (self, verbose, dry_run, force)
209 versions = get_devstudio_versions ()
210
211 if versions:
212 version = versions[0] # highest version
213
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)
222 try:
223 for p in string.split(os.environ['path'],';'):
224 path.append(p)
225 except KeyError:
226 pass
227 os.environ['path'] = string.join(path,';')
228 else:
229 # devstudio not found in the registry
230 self.cc = "cl.exe"
231 self.linker = "link.exe"
232 self.lib = "lib.exe"
233 self.rc = "rc.exe"
234 self.mc = "mc.exe"
235
236 self.preprocess_options = None
237 self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', '/GX' ,
238 '/DNDEBUG']
239 self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/GX',
240 '/Z7', '/D_DEBUG']
241
242 self.ldflags_shared = ['/DLL', '/nologo', '/INCREMENTAL:NO']
243 self.ldflags_shared_debug = [
244 '/DLL', '/nologo', '/INCREMENTAL:no', '/pdb:None', '/DEBUG'
245 ]
246 self.ldflags_static = [ '/nologo']
247
248
249 # -- Worker methods ------------------------------------------------
250
251 def object_filenames (self,
252 source_filenames,
253 strip_dir=0,
254 output_dir=''):
255 # Copied from ccompiler.py, extended to return .res as 'object'-file
256 # for .rc input file
257 if output_dir is None: output_dir = ''
258 obj_names = []
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
264 # different lengths
265 raise CompileError ("Don't know how to compile %s" % src_name)
266 if strip_dir:
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))
274 else:
275 obj_names.append (os.path.join (output_dir,
276 base + self.obj_extension))
277 return obj_names
278
279 # object_filenames ()
280
281
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):
285
286 macros, objects, extra_postargs, pp_opts, build = \
287 self._setup_compile(output_dir, macros, include_dirs, sources,
288 depends, extra_postargs)
289
290 compile_opts = extra_preargs or []
291 compile_opts.append ('/c')
292 if debug:
293 compile_opts.extend(self.compile_options_debug)
294 else:
295 compile_opts.extend(self.compile_options)
296
297 for obj, (src, ext) in build.items():
298 if debug:
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)
303
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
310 input_opt = src
311 output_opt = "/fo" + obj
312 try:
313 self.spawn ([self.rc] + pp_opts +
314 [output_opt] + [input_opt])
315 except DistutilsExecError, msg:
316 raise CompileError, msg
317 continue
318 elif ext in self._mc_extensions:
319
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
325 # it includes
326 #
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.
331
332 h_dir = os.path.dirname (src)
333 rc_dir = os.path.dirname (obj)
334 try:
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])
343
344 except DistutilsExecError, msg:
345 raise CompileError, msg
346 continue
347 else:
348 # how to handle this file?
349 raise CompileError (
350 "Don't know how to compile %s to %s" % \
351 (src, obj))
352
353 output_opt = "/Fo" + obj
354 try:
355 self.spawn ([self.cc] + compile_opts + pp_opts +
356 [input_opt, output_opt] +
357 extra_postargs)
358 except DistutilsExecError, msg:
359 raise CompileError, msg
360
361 return objects
362
363 # compile ()
364
365
366 def create_static_lib (self,
367 objects,
368 output_libname,
369 output_dir=None,
370 debug=0,
371 target_lang=None):
372
373 (objects, output_dir) = self._fix_object_args (objects, output_dir)
374 output_filename = \
375 self.library_filename (output_libname, output_dir=output_dir)
376
377 if self._need_link (objects, output_filename):
378 lib_args = objects + ['/OUT:' + output_filename]
379 if debug:
380 pass # XXX what goes here?
381 try:
382 self.spawn ([self.lib] + lib_args)
383 except DistutilsExecError, msg:
384 raise LibError, msg
385
386 else:
387 log.debug("skipping %s (up-to-date)", output_filename)
388
389 # create_static_lib ()
390
391 def link (self,
392 target_desc,
393 objects,
394 output_filename,
395 output_dir=None,
396 libraries=None,
397 library_dirs=None,
398 runtime_library_dirs=None,
399 export_symbols=None,
400 debug=0,
401 extra_preargs=None,
402 extra_postargs=None,
403 build_temp=None,
404 target_lang=None):
405
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)
409
410 if runtime_library_dirs:
411 self.warn ("I don't know what to do with 'runtime_library_dirs': "
412 + str (runtime_library_dirs))
413
414 lib_opts = gen_lib_options (self,
415 library_dirs, runtime_library_dirs,
416 libraries)
417 if output_dir is not None:
418 output_filename = os.path.join (output_dir, output_filename)
419
420 if self._need_link (objects, output_filename):
421
422 if target_desc == CCompiler.EXECUTABLE:
423 if debug:
424 ldflags = self.ldflags_shared_debug[1:]
425 else:
426 ldflags = self.ldflags_shared[1:]
427 else:
428 if debug:
429 ldflags = self.ldflags_shared_debug
430 else:
431 ldflags = self.ldflags_shared
432
433 export_opts = []
434 for sym in (export_symbols or []):
435 export_opts.append("/EXPORT:" + sym)
436
437 ld_args = (ldflags + lib_opts + export_opts +
438 objects + ['/OUT:' + output_filename])
439
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)
452
453 if extra_preargs:
454 ld_args[:0] = extra_preargs
455 if extra_postargs:
456 ld_args.extend(extra_postargs)
457
458 self.mkpath (os.path.dirname (output_filename))
459 try:
460 self.spawn ([self.linker] + ld_args)
461 except DistutilsExecError, msg:
462 raise LinkError, msg
463
464 else:
465 log.debug("skipping %s (up-to-date)", output_filename)
466
467 # link ()
468
469
470 # -- Miscellaneous methods -----------------------------------------
471 # These are all used by the 'gen_lib_options() function, in
472 # ccompiler.py.
473
474 def library_dir_option (self, dir):
475 return "/LIBPATH:" + dir
476
477 def runtime_library_dir_option (self, dir):
478 raise DistutilsPlatformError, \
479 "don't know how to set runtime library search path for MSVC++"
480
481 def library_option (self, lib):
482 return self.library_filename (lib)
483
484
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.
488 if debug:
489 try_names = [lib + "_d", lib]
490 else:
491 try_names = [lib]
492 for dir in dirs:
493 for name in try_names:
494 libfile = os.path.join(dir, self.library_filename (name))
495 if os.path.exists(libfile):
496 return libfile
497 else:
498 # Oops, didn't find it in *any* of 'dirs'
499 return None
500
501 # find_library_file ()
502
503 # class MSVCCompiler