py-cvs-rel2_1 (Rev 1.2) merge
[python/dscho.git] / Lib / distutils / msvccompiler.py
blob0325b485089e38fefaf44f488cea3ec74c28d3f9
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)
11 __revision__ = "$Id$"
13 import sys, os, string
14 from types import *
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
21 _can_read_reg = 0
22 try:
23 import _winreg
25 _can_read_reg = 1
26 hkey_mod = _winreg
28 RegOpenKeyEx = _winreg.OpenKeyEx
29 RegEnumKey = _winreg.EnumKey
30 RegEnumValue = _winreg.EnumValue
31 RegError = _winreg.error
33 except ImportError:
34 try:
35 import win32api
36 import win32con
37 _can_read_reg = 1
38 hkey_mod = win32con
40 RegOpenKeyEx = win32api.RegOpenKeyEx
41 RegEnumKey = win32api.RegEnumKey
42 RegEnumValue = win32api.RegEnumValue
43 RegError = win32api.error
45 except ImportError:
46 pass
48 if _can_read_reg:
49 HKEY_CLASSES_ROOT = hkey_mod.HKEY_CLASSES_ROOT
50 HKEY_LOCAL_MACHINE = hkey_mod.HKEY_LOCAL_MACHINE
51 HKEY_CURRENT_USER = hkey_mod.HKEY_CURRENT_USER
52 HKEY_USERS = hkey_mod.HKEY_USERS
56 def get_devstudio_versions ():
57 """Get list of devstudio versions from the Windows registry. Return a
58 list of strings containing version numbers; the list will be
59 empty if we were unable to access the registry (eg. couldn't import
60 a registry-access module) or the appropriate registry keys weren't
61 found."""
63 if not _can_read_reg:
64 return []
66 K = 'Software\\Microsoft\\Devstudio'
67 L = []
68 for base in (HKEY_CLASSES_ROOT,
69 HKEY_LOCAL_MACHINE,
70 HKEY_CURRENT_USER,
71 HKEY_USERS):
72 try:
73 k = RegOpenKeyEx(base,K)
74 i = 0
75 while 1:
76 try:
77 p = RegEnumKey(k,i)
78 if p[0] in '123456789' and p not in L:
79 L.append(p)
80 except RegError:
81 break
82 i = i + 1
83 except RegError:
84 pass
85 L.sort()
86 L.reverse()
87 return L
89 # get_devstudio_versions ()
92 def get_msvc_paths (path, version='6.0', platform='x86'):
93 """Get a list of devstudio directories (include, lib or path). Return
94 a list of strings; will be empty list if unable to access the
95 registry or appropriate registry keys not found."""
97 if not _can_read_reg:
98 return []
100 L = []
101 if path=='lib':
102 path= 'Library'
103 path = string.upper(path + ' Dirs')
104 K = ('Software\\Microsoft\\Devstudio\\%s\\' +
105 'Build System\\Components\\Platforms\\Win32 (%s)\\Directories') % \
106 (version,platform)
107 for base in (HKEY_CLASSES_ROOT,
108 HKEY_LOCAL_MACHINE,
109 HKEY_CURRENT_USER,
110 HKEY_USERS):
111 try:
112 k = RegOpenKeyEx(base,K)
113 i = 0
114 while 1:
115 try:
116 (p,v,t) = RegEnumValue(k,i)
117 if string.upper(p) == path:
118 V = string.split(v,';')
119 for v in V:
120 if v == '' or v in L: continue
121 L.append(v)
122 break
123 i = i + 1
124 except RegError:
125 break
126 except RegError:
127 pass
128 return L
130 # get_msvc_paths()
133 def find_exe (exe, version_number):
134 """Try to find an MSVC executable program 'exe' (from version
135 'version_number' of MSVC) in several places: first, one of the MSVC
136 program search paths from the registry; next, the directories in the
137 PATH environment variable. If any of those work, return an absolute
138 path that is known to exist. If none of them work, just return the
139 original program name, 'exe'."""
141 for p in get_msvc_paths ('path', version_number):
142 fn = os.path.join (os.path.abspath(p), exe)
143 if os.path.isfile(fn):
144 return fn
146 # didn't find it; try existing path
147 for p in string.split (os.environ['Path'],';'):
148 fn = os.path.join(os.path.abspath(p),exe)
149 if os.path.isfile(fn):
150 return fn
152 return exe # last desperate hope
155 def set_path_env_var (name, version_number):
156 """Set environment variable 'name' to an MSVC path type value obtained
157 from 'get_msvc_paths()'. This is equivalent to a SET command prior
158 to execution of spawned commands."""
160 p = get_msvc_paths (name, version_number)
161 if p:
162 os.environ[name] = string.join (p,';')
165 class MSVCCompiler (CCompiler) :
166 """Concrete class that implements an interface to Microsoft Visual C++,
167 as defined by the CCompiler abstract class."""
169 compiler_type = 'msvc'
171 # Just set this so CCompiler's constructor doesn't barf. We currently
172 # don't use the 'set_executables()' bureaucracy provided by CCompiler,
173 # as it really isn't necessary for this sort of single-compiler class.
174 # Would be nice to have a consistent interface with UnixCCompiler,
175 # though, so it's worth thinking about.
176 executables = {}
178 # Private class data (need to distinguish C from C++ source for compiler)
179 _c_extensions = ['.c']
180 _cpp_extensions = ['.cc', '.cpp', '.cxx']
181 _rc_extensions = ['.rc']
182 _mc_extensions = ['.mc']
184 # Needed for the filename generation methods provided by the
185 # base class, CCompiler.
186 src_extensions = (_c_extensions + _cpp_extensions +
187 _rc_extensions + _mc_extensions)
188 res_extension = '.res'
189 obj_extension = '.obj'
190 static_lib_extension = '.lib'
191 shared_lib_extension = '.dll'
192 static_lib_format = shared_lib_format = '%s%s'
193 exe_extension = '.exe'
196 def __init__ (self,
197 verbose=0,
198 dry_run=0,
199 force=0):
201 CCompiler.__init__ (self, verbose, dry_run, force)
202 versions = get_devstudio_versions ()
204 if versions:
205 version = versions[0] # highest version
207 self.cc = find_exe("cl.exe", version)
208 self.linker = find_exe("link.exe", version)
209 self.lib = find_exe("lib.exe", version)
210 self.rc = find_exe("rc.exe", version) # resource compiler
211 self.mc = find_exe("mc.exe", version) # message compiler
212 set_path_env_var ('lib', version)
213 set_path_env_var ('include', version)
214 path=get_msvc_paths('path', version)
215 try:
216 for p in string.split(os.environ['path'],';'):
217 path.append(p)
218 except KeyError:
219 pass
220 os.environ['path'] = string.join(path,';')
221 else:
222 # devstudio not found in the registry
223 self.cc = "cl.exe"
224 self.linker = "link.exe"
225 self.lib = "lib.exe"
226 self.rc = "rc.exe"
227 self.mc = "mc.exe"
229 self.preprocess_options = None
230 self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', '/GX' ]
231 self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/GX',
232 '/Z7', '/D_DEBUG']
234 self.ldflags_shared = ['/DLL', '/nologo', '/INCREMENTAL:NO']
235 self.ldflags_shared_debug = [
236 '/DLL', '/nologo', '/INCREMENTAL:no', '/pdb:None', '/DEBUG'
238 self.ldflags_static = [ '/nologo']
241 # -- Worker methods ------------------------------------------------
243 def object_filenames (self,
244 source_filenames,
245 strip_dir=0,
246 output_dir=''):
247 # Copied from ccompiler.py, extended to return .res as 'object'-file
248 # for .rc input file
249 if output_dir is None: output_dir = ''
250 obj_names = []
251 for src_name in source_filenames:
252 (base, ext) = os.path.splitext (src_name)
253 if ext not in self.src_extensions:
254 # Better to raise an exception instead of silently continuing
255 # and later complain about sources and targets having
256 # different lengths
257 raise CompileError ("Don't know how to compile %s" % src_name)
258 if strip_dir:
259 base = os.path.basename (base)
260 if ext in self._rc_extensions:
261 obj_names.append (os.path.join (output_dir,
262 base + self.res_extension))
263 elif ext in self._mc_extensions:
264 obj_names.append (os.path.join (output_dir,
265 base + self.res_extension))
266 else:
267 obj_names.append (os.path.join (output_dir,
268 base + self.obj_extension))
269 return obj_names
271 # object_filenames ()
274 def compile (self,
275 sources,
276 output_dir=None,
277 macros=None,
278 include_dirs=None,
279 debug=0,
280 extra_preargs=None,
281 extra_postargs=None):
283 (output_dir, macros, include_dirs) = \
284 self._fix_compile_args (output_dir, macros, include_dirs)
285 (objects, skip_sources) = self._prep_compile (sources, output_dir)
287 if extra_postargs is None:
288 extra_postargs = []
290 pp_opts = gen_preprocess_options (macros, include_dirs)
291 compile_opts = extra_preargs or []
292 compile_opts.append ('/c')
293 if debug:
294 compile_opts.extend (self.compile_options_debug)
295 else:
296 compile_opts.extend (self.compile_options)
298 for i in range (len (sources)):
299 src = sources[i] ; obj = objects[i]
300 ext = (os.path.splitext (src))[1]
302 if skip_sources[src]:
303 self.announce ("skipping %s (%s up-to-date)" % (src, obj))
304 else:
305 self.mkpath (os.path.dirname (obj))
307 if ext in self._c_extensions:
308 input_opt = "/Tc" + src
309 elif ext in self._cpp_extensions:
310 input_opt = "/Tp" + src
311 elif ext in self._rc_extensions:
312 # compile .RC to .RES file
313 input_opt = src
314 output_opt = "/fo" + obj
315 try:
316 self.spawn ([self.rc] +
317 [output_opt] + [input_opt])
318 except DistutilsExecError, msg:
319 raise CompileError, msg
320 continue
321 elif ext in self._mc_extensions:
323 # Compile .MC to .RC file to .RES file.
324 # * '-h dir' specifies the directory for the
325 # generated include file
326 # * '-r dir' specifies the target directory of the
327 # generated RC file and the binary message resource
328 # it includes
330 # For now (since there are no options to change this),
331 # we use the source-directory for the include file and
332 # the build directory for the RC file and message
333 # resources. This works at least for win32all.
335 h_dir = os.path.dirname (src)
336 rc_dir = os.path.dirname (obj)
337 try:
338 # first compile .MC to .RC and .H file
339 self.spawn ([self.mc] +
340 ['-h', h_dir, '-r', rc_dir] + [src])
341 base, _ = os.path.splitext (os.path.basename (src))
342 rc_file = os.path.join (rc_dir, base + '.rc')
343 # then compile .RC to .RES file
344 self.spawn ([self.rc] +
345 ["/fo" + obj] + [rc_file])
347 except DistutilsExecError, msg:
348 raise CompileError, msg
349 continue
350 else:
351 # how to handle this file?
352 raise CompileError (
353 "Don't know how to compile %s to %s" % \
354 (src, obj))
356 output_opt = "/Fo" + obj
357 try:
358 self.spawn ([self.cc] + compile_opts + pp_opts +
359 [input_opt, output_opt] +
360 extra_postargs)
361 except DistutilsExecError, msg:
362 raise CompileError, msg
364 return objects
366 # compile ()
369 def create_static_lib (self,
370 objects,
371 output_libname,
372 output_dir=None,
373 debug=0,
374 extra_preargs=None,
375 extra_postargs=None):
377 (objects, output_dir) = self._fix_object_args (objects, output_dir)
378 output_filename = \
379 self.library_filename (output_libname, output_dir=output_dir)
381 if self._need_link (objects, output_filename):
382 lib_args = objects + ['/OUT:' + output_filename]
383 if debug:
384 pass # XXX what goes here?
385 if extra_preargs:
386 lib_args[:0] = extra_preargs
387 if extra_postargs:
388 lib_args.extend (extra_postargs)
389 try:
390 self.spawn ([self.lib] + lib_args)
391 except DistutilsExecError, msg:
392 raise LibError, msg
394 else:
395 self.announce ("skipping %s (up-to-date)" % output_filename)
397 # create_static_lib ()
399 def link (self,
400 target_desc,
401 objects,
402 output_filename,
403 output_dir=None,
404 libraries=None,
405 library_dirs=None,
406 runtime_library_dirs=None,
407 export_symbols=None,
408 debug=0,
409 extra_preargs=None,
410 extra_postargs=None,
411 build_temp=None):
413 (objects, output_dir) = self._fix_object_args (objects, output_dir)
414 (libraries, library_dirs, runtime_library_dirs) = \
415 self._fix_lib_args (libraries, library_dirs, runtime_library_dirs)
417 if runtime_library_dirs:
418 self.warn ("I don't know what to do with 'runtime_library_dirs': "
419 + str (runtime_library_dirs))
421 lib_opts = gen_lib_options (self,
422 library_dirs, runtime_library_dirs,
423 libraries)
424 if output_dir is not None:
425 output_filename = os.path.join (output_dir, output_filename)
427 if self._need_link (objects, output_filename):
429 if target_desc == CCompiler.EXECUTABLE:
430 if debug:
431 ldflags = self.ldflags_shared_debug[1:]
432 else:
433 ldflags = self.ldflags_shared[1:]
434 else:
435 if debug:
436 ldflags = self.ldflags_shared_debug
437 else:
438 ldflags = self.ldflags_shared
440 export_opts = []
441 for sym in (export_symbols or []):
442 export_opts.append("/EXPORT:" + sym)
444 ld_args = (ldflags + lib_opts + export_opts +
445 objects + ['/OUT:' + output_filename])
447 # The MSVC linker generates .lib and .exp files, which cannot be
448 # suppressed by any linker switches. The .lib files may even be
449 # needed! Make sure they are generated in the temporary build
450 # directory. Since they have different names for debug and release
451 # builds, they can go into the same directory.
452 if export_symbols is not None:
453 (dll_name, dll_ext) = os.path.splitext(
454 os.path.basename(output_filename))
455 implib_file = os.path.join(
456 os.path.dirname(objects[0]),
457 self.library_filename(dll_name))
458 ld_args.append ('/IMPLIB:' + implib_file)
460 if extra_preargs:
461 ld_args[:0] = extra_preargs
462 if extra_postargs:
463 ld_args.extend(extra_postargs)
465 self.mkpath (os.path.dirname (output_filename))
466 try:
467 self.spawn ([self.linker] + ld_args)
468 except DistutilsExecError, msg:
469 raise LinkError, msg
471 else:
472 self.announce ("skipping %s (up-to-date)" % output_filename)
474 # link ()
477 # -- Miscellaneous methods -----------------------------------------
478 # These are all used by the 'gen_lib_options() function, in
479 # ccompiler.py.
481 def library_dir_option (self, dir):
482 return "/LIBPATH:" + dir
484 def runtime_library_dir_option (self, dir):
485 raise DistutilsPlatformError, \
486 "don't know how to set runtime library search path for MSVC++"
488 def library_option (self, lib):
489 return self.library_filename (lib)
492 def find_library_file (self, dirs, lib, debug=0):
493 # Prefer a debugging library if found (and requested), but deal
494 # with it if we don't have one.
495 if debug:
496 try_names = [lib + "_d", lib]
497 else:
498 try_names = [lib]
499 for dir in dirs:
500 for name in try_names:
501 libfile = os.path.join(dir, self.library_filename (name))
502 if os.path.exists(libfile):
503 return libfile
504 else:
505 # Oops, didn't find it in *any* of 'dirs'
506 return None
508 # find_library_file ()
510 # class MSVCCompiler