Commit | Line | Data |
---|---|---|
920dae64 AT |
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 2.1. | |
12 | ||
13 | __revision__ = "$Id: msvccompiler.py,v 1.64.2.4 2005/08/07 20:50:37 loewis Exp $" | |
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 | try: | |
128 | if version > 7.0: | |
129 | self.set_macro("FrameworkSDKDir", net, "sdkinstallrootv1.1") | |
130 | else: | |
131 | self.set_macro("FrameworkSDKDir", net, "sdkinstallroot") | |
132 | except KeyError, exc: # | |
133 | raise DistutilsPlatformError, \ | |
134 | ("The .NET Framework SDK needs to be installed before " | |
135 | "building extensions for Python.") | |
136 | ||
137 | p = r"Software\Microsoft\NET Framework Setup\Product" | |
138 | for base in HKEYS: | |
139 | try: | |
140 | h = RegOpenKeyEx(base, p) | |
141 | except RegError: | |
142 | continue | |
143 | key = RegEnumKey(h, 0) | |
144 | d = read_values(base, r"%s\%s" % (p, key)) | |
145 | self.macros["$(FrameworkVersion)"] = d["version"] | |
146 | ||
147 | def sub(self, s): | |
148 | for k, v in self.macros.items(): | |
149 | s = string.replace(s, k, v) | |
150 | return s | |
151 | ||
152 | def get_build_version(): | |
153 | """Return the version of MSVC that was used to build Python. | |
154 | ||
155 | For Python 2.3 and up, the version number is included in | |
156 | sys.version. For earlier versions, assume the compiler is MSVC 6. | |
157 | """ | |
158 | ||
159 | prefix = "MSC v." | |
160 | i = string.find(sys.version, prefix) | |
161 | if i == -1: | |
162 | return 6 | |
163 | i = i + len(prefix) | |
164 | s, rest = sys.version[i:].split(" ", 1) | |
165 | majorVersion = int(s[:-2]) - 6 | |
166 | minorVersion = int(s[2:3]) / 10.0 | |
167 | # I don't think paths are affected by minor version in version 6 | |
168 | if majorVersion == 6: | |
169 | minorVersion = 0 | |
170 | if majorVersion >= 6: | |
171 | return majorVersion + minorVersion | |
172 | # else we don't know what version of the compiler this is | |
173 | return None | |
174 | ||
175 | ||
176 | class MSVCCompiler (CCompiler) : | |
177 | """Concrete class that implements an interface to Microsoft Visual C++, | |
178 | as defined by the CCompiler abstract class.""" | |
179 | ||
180 | compiler_type = 'msvc' | |
181 | ||
182 | # Just set this so CCompiler's constructor doesn't barf. We currently | |
183 | # don't use the 'set_executables()' bureaucracy provided by CCompiler, | |
184 | # as it really isn't necessary for this sort of single-compiler class. | |
185 | # Would be nice to have a consistent interface with UnixCCompiler, | |
186 | # though, so it's worth thinking about. | |
187 | executables = {} | |
188 | ||
189 | # Private class data (need to distinguish C from C++ source for compiler) | |
190 | _c_extensions = ['.c'] | |
191 | _cpp_extensions = ['.cc', '.cpp', '.cxx'] | |
192 | _rc_extensions = ['.rc'] | |
193 | _mc_extensions = ['.mc'] | |
194 | ||
195 | # Needed for the filename generation methods provided by the | |
196 | # base class, CCompiler. | |
197 | src_extensions = (_c_extensions + _cpp_extensions + | |
198 | _rc_extensions + _mc_extensions) | |
199 | res_extension = '.res' | |
200 | obj_extension = '.obj' | |
201 | static_lib_extension = '.lib' | |
202 | shared_lib_extension = '.dll' | |
203 | static_lib_format = shared_lib_format = '%s%s' | |
204 | exe_extension = '.exe' | |
205 | ||
206 | def __init__ (self, verbose=0, dry_run=0, force=0): | |
207 | CCompiler.__init__ (self, verbose, dry_run, force) | |
208 | self.__version = get_build_version() | |
209 | if self.__version >= 7: | |
210 | self.__root = r"Software\Microsoft\VisualStudio" | |
211 | self.__macros = MacroExpander(self.__version) | |
212 | else: | |
213 | self.__root = r"Software\Microsoft\Devstudio" | |
214 | self.initialized = False | |
215 | ||
216 | def initialize(self): | |
217 | self.__paths = self.get_msvc_paths("path") | |
218 | ||
219 | if len (self.__paths) == 0: | |
220 | raise DistutilsPlatformError, \ | |
221 | ("Python was built with version %s of Visual Studio, " | |
222 | "and extensions need to be built with the same " | |
223 | "version of the compiler, but it isn't installed." % self.__version) | |
224 | ||
225 | self.cc = self.find_exe("cl.exe") | |
226 | self.linker = self.find_exe("link.exe") | |
227 | self.lib = self.find_exe("lib.exe") | |
228 | self.rc = self.find_exe("rc.exe") # resource compiler | |
229 | self.mc = self.find_exe("mc.exe") # message compiler | |
230 | self.set_path_env_var('lib') | |
231 | self.set_path_env_var('include') | |
232 | ||
233 | # extend the MSVC path with the current path | |
234 | try: | |
235 | for p in string.split(os.environ['path'], ';'): | |
236 | self.__paths.append(p) | |
237 | except KeyError: | |
238 | pass | |
239 | os.environ['path'] = string.join(self.__paths, ';') | |
240 | ||
241 | self.preprocess_options = None | |
242 | self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', '/GX' , | |
243 | '/DNDEBUG'] | |
244 | self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/GX', | |
245 | '/Z7', '/D_DEBUG'] | |
246 | ||
247 | self.ldflags_shared = ['/DLL', '/nologo', '/INCREMENTAL:NO'] | |
248 | if self.__version >= 7: | |
249 | self.ldflags_shared_debug = [ | |
250 | '/DLL', '/nologo', '/INCREMENTAL:no', '/DEBUG' | |
251 | ] | |
252 | else: | |
253 | self.ldflags_shared_debug = [ | |
254 | '/DLL', '/nologo', '/INCREMENTAL:no', '/pdb:None', '/DEBUG' | |
255 | ] | |
256 | self.ldflags_static = [ '/nologo'] | |
257 | ||
258 | self.initialized = True | |
259 | ||
260 | ||
261 | # -- Worker methods ------------------------------------------------ | |
262 | ||
263 | def object_filenames (self, | |
264 | source_filenames, | |
265 | strip_dir=0, | |
266 | output_dir=''): | |
267 | # Copied from ccompiler.py, extended to return .res as 'object'-file | |
268 | # for .rc input file | |
269 | if output_dir is None: output_dir = '' | |
270 | obj_names = [] | |
271 | for src_name in source_filenames: | |
272 | (base, ext) = os.path.splitext (src_name) | |
273 | base = os.path.splitdrive(base)[1] # Chop off the drive | |
274 | base = base[os.path.isabs(base):] # If abs, chop off leading / | |
275 | if ext not in self.src_extensions: | |
276 | # Better to raise an exception instead of silently continuing | |
277 | # and later complain about sources and targets having | |
278 | # different lengths | |
279 | raise CompileError ("Don't know how to compile %s" % src_name) | |
280 | if strip_dir: | |
281 | base = os.path.basename (base) | |
282 | if ext in self._rc_extensions: | |
283 | obj_names.append (os.path.join (output_dir, | |
284 | base + self.res_extension)) | |
285 | elif ext in self._mc_extensions: | |
286 | obj_names.append (os.path.join (output_dir, | |
287 | base + self.res_extension)) | |
288 | else: | |
289 | obj_names.append (os.path.join (output_dir, | |
290 | base + self.obj_extension)) | |
291 | return obj_names | |
292 | ||
293 | # object_filenames () | |
294 | ||
295 | ||
296 | def compile(self, sources, | |
297 | output_dir=None, macros=None, include_dirs=None, debug=0, | |
298 | extra_preargs=None, extra_postargs=None, depends=None): | |
299 | ||
300 | if not self.initialized: self.initialize() | |
301 | macros, objects, extra_postargs, pp_opts, build = \ | |
302 | self._setup_compile(output_dir, macros, include_dirs, sources, | |
303 | depends, extra_postargs) | |
304 | ||
305 | compile_opts = extra_preargs or [] | |
306 | compile_opts.append ('/c') | |
307 | if debug: | |
308 | compile_opts.extend(self.compile_options_debug) | |
309 | else: | |
310 | compile_opts.extend(self.compile_options) | |
311 | ||
312 | for obj in objects: | |
313 | try: | |
314 | src, ext = build[obj] | |
315 | except KeyError: | |
316 | continue | |
317 | if debug: | |
318 | # pass the full pathname to MSVC in debug mode, | |
319 | # this allows the debugger to find the source file | |
320 | # without asking the user to browse for it | |
321 | src = os.path.abspath(src) | |
322 | ||
323 | if ext in self._c_extensions: | |
324 | input_opt = "/Tc" + src | |
325 | elif ext in self._cpp_extensions: | |
326 | input_opt = "/Tp" + src | |
327 | elif ext in self._rc_extensions: | |
328 | # compile .RC to .RES file | |
329 | input_opt = src | |
330 | output_opt = "/fo" + obj | |
331 | try: | |
332 | self.spawn ([self.rc] + pp_opts + | |
333 | [output_opt] + [input_opt]) | |
334 | except DistutilsExecError, msg: | |
335 | raise CompileError, msg | |
336 | continue | |
337 | elif ext in self._mc_extensions: | |
338 | ||
339 | # Compile .MC to .RC file to .RES file. | |
340 | # * '-h dir' specifies the directory for the | |
341 | # generated include file | |
342 | # * '-r dir' specifies the target directory of the | |
343 | # generated RC file and the binary message resource | |
344 | # it includes | |
345 | # | |
346 | # For now (since there are no options to change this), | |
347 | # we use the source-directory for the include file and | |
348 | # the build directory for the RC file and message | |
349 | # resources. This works at least for win32all. | |
350 | ||
351 | h_dir = os.path.dirname (src) | |
352 | rc_dir = os.path.dirname (obj) | |
353 | try: | |
354 | # first compile .MC to .RC and .H file | |
355 | self.spawn ([self.mc] + | |
356 | ['-h', h_dir, '-r', rc_dir] + [src]) | |
357 | base, _ = os.path.splitext (os.path.basename (src)) | |
358 | rc_file = os.path.join (rc_dir, base + '.rc') | |
359 | # then compile .RC to .RES file | |
360 | self.spawn ([self.rc] + | |
361 | ["/fo" + obj] + [rc_file]) | |
362 | ||
363 | except DistutilsExecError, msg: | |
364 | raise CompileError, msg | |
365 | continue | |
366 | else: | |
367 | # how to handle this file? | |
368 | raise CompileError ( | |
369 | "Don't know how to compile %s to %s" % \ | |
370 | (src, obj)) | |
371 | ||
372 | output_opt = "/Fo" + obj | |
373 | try: | |
374 | self.spawn ([self.cc] + compile_opts + pp_opts + | |
375 | [input_opt, output_opt] + | |
376 | extra_postargs) | |
377 | except DistutilsExecError, msg: | |
378 | raise CompileError, msg | |
379 | ||
380 | return objects | |
381 | ||
382 | # compile () | |
383 | ||
384 | ||
385 | def create_static_lib (self, | |
386 | objects, | |
387 | output_libname, | |
388 | output_dir=None, | |
389 | debug=0, | |
390 | target_lang=None): | |
391 | ||
392 | if not self.initialized: self.initialize() | |
393 | (objects, output_dir) = self._fix_object_args (objects, output_dir) | |
394 | output_filename = \ | |
395 | self.library_filename (output_libname, output_dir=output_dir) | |
396 | ||
397 | if self._need_link (objects, output_filename): | |
398 | lib_args = objects + ['/OUT:' + output_filename] | |
399 | if debug: | |
400 | pass # XXX what goes here? | |
401 | try: | |
402 | self.spawn ([self.lib] + lib_args) | |
403 | except DistutilsExecError, msg: | |
404 | raise LibError, msg | |
405 | ||
406 | else: | |
407 | log.debug("skipping %s (up-to-date)", output_filename) | |
408 | ||
409 | # create_static_lib () | |
410 | ||
411 | def link (self, | |
412 | target_desc, | |
413 | objects, | |
414 | output_filename, | |
415 | output_dir=None, | |
416 | libraries=None, | |
417 | library_dirs=None, | |
418 | runtime_library_dirs=None, | |
419 | export_symbols=None, | |
420 | debug=0, | |
421 | extra_preargs=None, | |
422 | extra_postargs=None, | |
423 | build_temp=None, | |
424 | target_lang=None): | |
425 | ||
426 | if not self.initialized: self.initialize() | |
427 | (objects, output_dir) = self._fix_object_args (objects, output_dir) | |
428 | (libraries, library_dirs, runtime_library_dirs) = \ | |
429 | self._fix_lib_args (libraries, library_dirs, runtime_library_dirs) | |
430 | ||
431 | if runtime_library_dirs: | |
432 | self.warn ("I don't know what to do with 'runtime_library_dirs': " | |
433 | + str (runtime_library_dirs)) | |
434 | ||
435 | lib_opts = gen_lib_options (self, | |
436 | library_dirs, runtime_library_dirs, | |
437 | libraries) | |
438 | if output_dir is not None: | |
439 | output_filename = os.path.join (output_dir, output_filename) | |
440 | ||
441 | if self._need_link (objects, output_filename): | |
442 | ||
443 | if target_desc == CCompiler.EXECUTABLE: | |
444 | if debug: | |
445 | ldflags = self.ldflags_shared_debug[1:] | |
446 | else: | |
447 | ldflags = self.ldflags_shared[1:] | |
448 | else: | |
449 | if debug: | |
450 | ldflags = self.ldflags_shared_debug | |
451 | else: | |
452 | ldflags = self.ldflags_shared | |
453 | ||
454 | export_opts = [] | |
455 | for sym in (export_symbols or []): | |
456 | export_opts.append("/EXPORT:" + sym) | |
457 | ||
458 | ld_args = (ldflags + lib_opts + export_opts + | |
459 | objects + ['/OUT:' + output_filename]) | |
460 | ||
461 | # The MSVC linker generates .lib and .exp files, which cannot be | |
462 | # suppressed by any linker switches. The .lib files may even be | |
463 | # needed! Make sure they are generated in the temporary build | |
464 | # directory. Since they have different names for debug and release | |
465 | # builds, they can go into the same directory. | |
466 | if export_symbols is not None: | |
467 | (dll_name, dll_ext) = os.path.splitext( | |
468 | os.path.basename(output_filename)) | |
469 | implib_file = os.path.join( | |
470 | os.path.dirname(objects[0]), | |
471 | self.library_filename(dll_name)) | |
472 | ld_args.append ('/IMPLIB:' + implib_file) | |
473 | ||
474 | if extra_preargs: | |
475 | ld_args[:0] = extra_preargs | |
476 | if extra_postargs: | |
477 | ld_args.extend(extra_postargs) | |
478 | ||
479 | self.mkpath (os.path.dirname (output_filename)) | |
480 | try: | |
481 | self.spawn ([self.linker] + ld_args) | |
482 | except DistutilsExecError, msg: | |
483 | raise LinkError, msg | |
484 | ||
485 | else: | |
486 | log.debug("skipping %s (up-to-date)", output_filename) | |
487 | ||
488 | # link () | |
489 | ||
490 | ||
491 | # -- Miscellaneous methods ----------------------------------------- | |
492 | # These are all used by the 'gen_lib_options() function, in | |
493 | # ccompiler.py. | |
494 | ||
495 | def library_dir_option (self, dir): | |
496 | return "/LIBPATH:" + dir | |
497 | ||
498 | def runtime_library_dir_option (self, dir): | |
499 | raise DistutilsPlatformError, \ | |
500 | "don't know how to set runtime library search path for MSVC++" | |
501 | ||
502 | def library_option (self, lib): | |
503 | return self.library_filename (lib) | |
504 | ||
505 | ||
506 | def find_library_file (self, dirs, lib, debug=0): | |
507 | # Prefer a debugging library if found (and requested), but deal | |
508 | # with it if we don't have one. | |
509 | if debug: | |
510 | try_names = [lib + "_d", lib] | |
511 | else: | |
512 | try_names = [lib] | |
513 | for dir in dirs: | |
514 | for name in try_names: | |
515 | libfile = os.path.join(dir, self.library_filename (name)) | |
516 | if os.path.exists(libfile): | |
517 | return libfile | |
518 | else: | |
519 | # Oops, didn't find it in *any* of 'dirs' | |
520 | return None | |
521 | ||
522 | # find_library_file () | |
523 | ||
524 | # Helper methods for using the MSVC registry settings | |
525 | ||
526 | def find_exe(self, exe): | |
527 | """Return path to an MSVC executable program. | |
528 | ||
529 | Tries to find the program in several places: first, one of the | |
530 | MSVC program search paths from the registry; next, the directories | |
531 | in the PATH environment variable. If any of those work, return an | |
532 | absolute path that is known to exist. If none of them work, just | |
533 | return the original program name, 'exe'. | |
534 | """ | |
535 | ||
536 | for p in self.__paths: | |
537 | fn = os.path.join(os.path.abspath(p), exe) | |
538 | if os.path.isfile(fn): | |
539 | return fn | |
540 | ||
541 | # didn't find it; try existing path | |
542 | for p in string.split(os.environ['Path'],';'): | |
543 | fn = os.path.join(os.path.abspath(p),exe) | |
544 | if os.path.isfile(fn): | |
545 | return fn | |
546 | ||
547 | return exe | |
548 | ||
549 | def get_msvc_paths(self, path, platform='x86'): | |
550 | """Get a list of devstudio directories (include, lib or path). | |
551 | ||
552 | Return a list of strings. The list will be empty if unable to | |
553 | access the registry or appropriate registry keys not found. | |
554 | """ | |
555 | ||
556 | if not _can_read_reg: | |
557 | return [] | |
558 | ||
559 | path = path + " dirs" | |
560 | if self.__version >= 7: | |
561 | key = (r"%s\%0.1f\VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories" | |
562 | % (self.__root, self.__version)) | |
563 | else: | |
564 | key = (r"%s\6.0\Build System\Components\Platforms" | |
565 | r"\Win32 (%s)\Directories" % (self.__root, platform)) | |
566 | ||
567 | for base in HKEYS: | |
568 | d = read_values(base, key) | |
569 | if d: | |
570 | if self.__version >= 7: | |
571 | return string.split(self.__macros.sub(d[path]), ";") | |
572 | else: | |
573 | return string.split(d[path], ";") | |
574 | # MSVC 6 seems to create the registry entries we need only when | |
575 | # the GUI is run. | |
576 | if self.__version == 6: | |
577 | for base in HKEYS: | |
578 | if read_values(base, r"%s\6.0" % self.__root) is not None: | |
579 | self.warn("It seems you have Visual Studio 6 installed, " | |
580 | "but the expected registry settings are not present.\n" | |
581 | "You must at least run the Visual Studio GUI once " | |
582 | "so that these entries are created.") | |
583 | break | |
584 | return [] | |
585 | ||
586 | def set_path_env_var(self, name): | |
587 | """Set environment variable 'name' to an MSVC path type value. | |
588 | ||
589 | This is equivalent to a SET command prior to execution of spawned | |
590 | commands. | |
591 | """ | |
592 | ||
593 | if name == "lib": | |
594 | p = self.get_msvc_paths("library") | |
595 | else: | |
596 | p = self.get_msvc_paths(name) | |
597 | if p: | |
598 | os.environ[name] = string.join(p, ';') |