]>
Commit | Line | Data |
---|---|---|
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 |