]> git.saurik.com Git - wxWidgets.git/blob - wxPython/distutils/msvccompiler.py
Latest distutils
[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
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)
10
11 # This module should be kept compatible with Python 1.5.2.
12
13 __revision__ = "$Id$"
14
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
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 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.")
52 pass
53
54 if _can_read_reg:
55 HKEYS = (hkey_mod.HKEY_USERS,
56 hkey_mod.HKEY_CURRENT_USER,
57 hkey_mod.HKEY_LOCAL_MACHINE,
58 hkey_mod.HKEY_CLASSES_ROOT)
59
60 def read_keys(base, key):
61 """Return list of registry keys."""
62
63 try:
64 handle = RegOpenKeyEx(base, key)
65 except RegError:
66 return None
67 L = []
68 i = 0
69 while 1:
70 try:
71 k = RegEnumKey(handle, i)
72 except RegError:
73 break
74 L.append(k)
75 i = i + 1
76 return L
77
78 def read_values(base, key):
79 """Return dict of registry keys and values.
80
81 All names are converted to lowercase.
82 """
83 try:
84 handle = RegOpenKeyEx(base, key)
85 except RegError:
86 return None
87 d = {}
88 i = 0
89 while 1:
90 try:
91 name, value, type = RegEnumValue(handle, i)
92 except RegError:
93 break
94 name = name.lower()
95 d[convert_mbcs(name)] = convert_mbcs(value)
96 i = i + 1
97 return d
98
99 def convert_mbcs(s):
100 enc = getattr(s, "encode", None)
101 if enc is not None:
102 try:
103 s = enc("mbcs")
104 except UnicodeError:
105 pass
106 return s
107
108 class MacroExpander:
109
110 def __init__(self, version):
111 self.macros = {}
112 self.load_macros(version)
113
114 def set_macro(self, macro, path, key):
115 for base in HKEYS:
116 d = read_values(base, path)
117 if d:
118 self.macros["$(%s)" % macro] = d[key]
119 break
120
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")
127 if version > 7.0:
128 self.set_macro("FrameworkSDKDir", net, "sdkinstallrootv1.1")
129 else:
130 self.set_macro("FrameworkSDKDir", net, "sdkinstallroot")
131
132 p = r"Software\Microsoft\NET Framework Setup\Product"
133 for base in HKEYS:
134 try:
135 h = RegOpenKeyEx(base, p)
136 except RegError:
137 continue
138 key = RegEnumKey(h, 0)
139 d = read_values(base, r"%s\%s" % (p, key))
140 self.macros["$(FrameworkVersion)"] = d["version"]
141
142 def sub(self, s):
143 for k, v in self.macros.items():
144 s = string.replace(s, k, v)
145 return s
146
147 def get_build_version():
148 """Return the version of MSVC that was used to build Python.
149
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.
152 """
153
154 prefix = "MSC v."
155 i = string.find(sys.version, prefix)
156 if i == -1:
157 return 6
158 i = i + len(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:
164 minorVersion = 0
165 if majorVersion >= 6:
166 return majorVersion + minorVersion
167 # else we don't know what version of the compiler this is
168 return None
169
170
171 class MSVCCompiler (CCompiler) :
172 """Concrete class that implements an interface to Microsoft Visual C++,
173 as defined by the CCompiler abstract class."""
174
175 compiler_type = 'msvc'
176
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.
182 executables = {}
183
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']
189
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'
200
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)
207 else:
208 self.__root = r"Software\Microsoft\Devstudio"
209 self.__paths = self.get_msvc_paths("path")
210
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)
216
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')
224
225 # extend the MSVC path with the current path
226 try:
227 for p in string.split(os.environ['path'], ';'):
228 self.__paths.append(p)
229 except KeyError:
230 pass
231 os.environ['path'] = string.join(self.__paths, ';')
232
233 self.preprocess_options = None
234 self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', '/GX' ,
235 '/DNDEBUG']
236 self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/GX',
237 '/Z7', '/D_DEBUG']
238
239 self.ldflags_shared = ['/DLL', '/nologo', '/INCREMENTAL:NO']
240 self.ldflags_shared_debug = [
241 '/DLL', '/nologo', '/INCREMENTAL:no', '/pdb:None', '/DEBUG'
242 ]
243 self.ldflags_static = [ '/nologo']
244
245
246 # -- Worker methods ------------------------------------------------
247
248 def object_filenames (self,
249 source_filenames,
250 strip_dir=0,
251 output_dir=''):
252 # Copied from ccompiler.py, extended to return .res as 'object'-file
253 # for .rc input file
254 if output_dir is None: output_dir = ''
255 obj_names = []
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
261 # different lengths
262 raise CompileError ("Don't know how to compile %s" % src_name)
263 if strip_dir:
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))
271 else:
272 obj_names.append (os.path.join (output_dir,
273 base + self.obj_extension))
274 return obj_names
275
276 # object_filenames ()
277
278
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):
282
283 macros, objects, extra_postargs, pp_opts, build = \
284 self._setup_compile(output_dir, macros, include_dirs, sources,
285 depends, extra_postargs)
286
287 compile_opts = extra_preargs or []
288 compile_opts.append ('/c')
289 if debug:
290 compile_opts.extend(self.compile_options_debug)
291 else:
292 compile_opts.extend(self.compile_options)
293
294 for obj, (src, ext) in build.items():
295 if debug:
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)
300
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
307 input_opt = src
308 output_opt = "/fo" + obj
309 try:
310 self.spawn ([self.rc] + pp_opts +
311 [output_opt] + [input_opt])
312 except DistutilsExecError, msg:
313 raise CompileError, msg
314 continue
315 elif ext in self._mc_extensions:
316
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
322 # it includes
323 #
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.
328
329 h_dir = os.path.dirname (src)
330 rc_dir = os.path.dirname (obj)
331 try:
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])
340
341 except DistutilsExecError, msg:
342 raise CompileError, msg
343 continue
344 else:
345 # how to handle this file?
346 raise CompileError (
347 "Don't know how to compile %s to %s" % \
348 (src, obj))
349
350 output_opt = "/Fo" + obj
351 try:
352 self.spawn ([self.cc] + compile_opts + pp_opts +
353 [input_opt, output_opt] +
354 extra_postargs)
355 except DistutilsExecError, msg:
356 raise CompileError, msg
357
358 return objects
359
360 # compile ()
361
362
363 def create_static_lib (self,
364 objects,
365 output_libname,
366 output_dir=None,
367 debug=0,
368 target_lang=None):
369
370 (objects, output_dir) = self._fix_object_args (objects, output_dir)
371 output_filename = \
372 self.library_filename (output_libname, output_dir=output_dir)
373
374 if self._need_link (objects, output_filename):
375 lib_args = objects + ['/OUT:' + output_filename]
376 if debug:
377 pass # XXX what goes here?
378 try:
379 self.spawn ([self.lib] + lib_args)
380 except DistutilsExecError, msg:
381 raise LibError, msg
382
383 else:
384 log.debug("skipping %s (up-to-date)", output_filename)
385
386 # create_static_lib ()
387
388 def link (self,
389 target_desc,
390 objects,
391 output_filename,
392 output_dir=None,
393 libraries=None,
394 library_dirs=None,
395 runtime_library_dirs=None,
396 export_symbols=None,
397 debug=0,
398 extra_preargs=None,
399 extra_postargs=None,
400 build_temp=None,
401 target_lang=None):
402
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)
406
407 if runtime_library_dirs:
408 self.warn ("I don't know what to do with 'runtime_library_dirs': "
409 + str (runtime_library_dirs))
410
411 lib_opts = gen_lib_options (self,
412 library_dirs, runtime_library_dirs,
413 libraries)
414 if output_dir is not None:
415 output_filename = os.path.join (output_dir, output_filename)
416
417 if self._need_link (objects, output_filename):
418
419 if target_desc == CCompiler.EXECUTABLE:
420 if debug:
421 ldflags = self.ldflags_shared_debug[1:]
422 else:
423 ldflags = self.ldflags_shared[1:]
424 else:
425 if debug:
426 ldflags = self.ldflags_shared_debug
427 else:
428 ldflags = self.ldflags_shared
429
430 export_opts = []
431 for sym in (export_symbols or []):
432 export_opts.append("/EXPORT:" + sym)
433
434 ld_args = (ldflags + lib_opts + export_opts +
435 objects + ['/OUT:' + output_filename])
436
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)
449
450 if extra_preargs:
451 ld_args[:0] = extra_preargs
452 if extra_postargs:
453 ld_args.extend(extra_postargs)
454
455 self.mkpath (os.path.dirname (output_filename))
456 try:
457 self.spawn ([self.linker] + ld_args)
458 except DistutilsExecError, msg:
459 raise LinkError, msg
460
461 else:
462 log.debug("skipping %s (up-to-date)", output_filename)
463
464 # link ()
465
466
467 # -- Miscellaneous methods -----------------------------------------
468 # These are all used by the 'gen_lib_options() function, in
469 # ccompiler.py.
470
471 def library_dir_option (self, dir):
472 return "/LIBPATH:" + dir
473
474 def runtime_library_dir_option (self, dir):
475 raise DistutilsPlatformError, \
476 "don't know how to set runtime library search path for MSVC++"
477
478 def library_option (self, lib):
479 return self.library_filename (lib)
480
481
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.
485 if debug:
486 try_names = [lib + "_d", lib]
487 else:
488 try_names = [lib]
489 for dir in dirs:
490 for name in try_names:
491 libfile = os.path.join(dir, self.library_filename (name))
492 if os.path.exists(libfile):
493 return libfile
494 else:
495 # Oops, didn't find it in *any* of 'dirs'
496 return None
497
498 # find_library_file ()
499
500 # Helper methods for using the MSVC registry settings
501
502 def find_exe(self, exe):
503 """Return path to an MSVC executable program.
504
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'.
510 """
511
512 for p in self.__paths:
513 fn = os.path.join(os.path.abspath(p), exe)
514 if os.path.isfile(fn):
515 return fn
516
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):
521 return fn
522
523 return exe
524
525 def get_msvc_paths(self, path, platform='x86'):
526 """Get a list of devstudio directories (include, lib or path).
527
528 Return a list of strings. The list will be empty if unable to
529 access the registry or appropriate registry keys not found.
530 """
531
532 if not _can_read_reg:
533 return []
534
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))
539 else:
540 key = (r"%s\6.0\Build System\Components\Platforms"
541 r"\Win32 (%s)\Directories" % (self.__root, platform))
542
543 for base in HKEYS:
544 d = read_values(base, key)
545 if d:
546 if self.__version >= 7:
547 return string.split(self.__macros.sub(d[path]), ";")
548 else:
549 return string.split(d[path], ";")
550 return []
551
552 def set_path_env_var(self, name):
553 """Set environment variable 'name' to an MSVC path type value.
554
555 This is equivalent to a SET command prior to execution of spawned
556 commands.
557 """
558
559 if name == "lib":
560 p = self.get_msvc_paths("library")
561 else:
562 p = self.get_msvc_paths(name)
563 if p:
564 os.environ[name] = string.join(p, ';')
565