blob: 97f76bb99f1edbd3a385666cfc7542a409ad0531 [file] [log] [blame]
Tarek Ziade1231a4e2011-05-19 13:07:25 +02001"""CCompiler implementation for old Microsoft Visual Studio compilers.
2
3For a compiler compatible with VS 2005 and 2008, use msvc9compiler.
4"""
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
11import sys
12import os
13
14from packaging.errors import (PackagingExecError, PackagingPlatformError,
15 CompileError, LibError, LinkError)
16from packaging.compiler.ccompiler import CCompiler
17from packaging.compiler import gen_lib_options
18from packaging import logger
19
20_can_read_reg = False
21try:
22 import winreg
23
24 _can_read_reg = True
25 hkey_mod = winreg
26
27 RegOpenKeyEx = winreg.OpenKeyEx
28 RegEnumKey = winreg.EnumKey
29 RegEnumValue = winreg.EnumValue
30 RegError = winreg.error
31
32except ImportError:
33 try:
34 import win32api
35 import win32con
36 _can_read_reg = True
37 hkey_mod = win32con
38
39 RegOpenKeyEx = win32api.RegOpenKeyEx
40 RegEnumKey = win32api.RegEnumKey
41 RegEnumValue = win32api.RegEnumValue
42 RegError = win32api.error
43
44 except ImportError:
45 logger.warning(
46 "can't read registry to find the necessary compiler setting;\n"
47 "make sure that Python modules _winreg, win32api or win32con "
48 "are installed.")
49
50if _can_read_reg:
51 HKEYS = (hkey_mod.HKEY_USERS,
52 hkey_mod.HKEY_CURRENT_USER,
53 hkey_mod.HKEY_LOCAL_MACHINE,
54 hkey_mod.HKEY_CLASSES_ROOT)
55
56
57def read_keys(base, key):
58 """Return list of registry keys."""
59
60 try:
61 handle = RegOpenKeyEx(base, key)
62 except RegError:
63 return None
64 L = []
65 i = 0
66 while True:
67 try:
68 k = RegEnumKey(handle, i)
69 except RegError:
70 break
71 L.append(k)
72 i = i + 1
73 return L
74
75
76def read_values(base, key):
77 """Return dict of registry keys and values.
78
79 All names are converted to lowercase.
80 """
81 try:
82 handle = RegOpenKeyEx(base, key)
83 except RegError:
84 return None
85 d = {}
86 i = 0
87 while True:
88 try:
89 name, value, type = RegEnumValue(handle, i)
90 except RegError:
91 break
92 name = name.lower()
93 d[convert_mbcs(name)] = convert_mbcs(value)
94 i = i + 1
95 return d
96
97
98def convert_mbcs(s):
99 enc = getattr(s, "encode", None)
100 if enc is not None:
101 try:
102 s = enc("mbcs")
103 except UnicodeError:
104 pass
105 return s
106
107
108class 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:
133 raise PackagingPlatformError(
134"""Python was built with Visual Studio 2003; extensions must be built with
135a compiler than can generate compatible binaries. Visual Studio 2003 was
136not found on this system. If you have Cygwin installed, you can try
137compiling with MingW32, by passing "-c mingw32" to setup.py.""")
138# XXX update this comment for setup.cfg
139
140 p = r"Software\Microsoft\NET Framework Setup\Product"
141 for base in HKEYS:
142 try:
143 h = RegOpenKeyEx(base, p)
144 except RegError:
145 continue
146 key = RegEnumKey(h, 0)
147 d = read_values(base, r"%s\%s" % (p, key))
148 self.macros["$(FrameworkVersion)"] = d["version"]
149
150 def sub(self, s):
151 for k, v in self.macros.items():
152 s = s.replace(k, v)
153 return s
154
155
156def get_build_version():
157 """Return the version of MSVC that was used to build Python.
158
159 For Python 2.3 and up, the version number is included in
160 sys.version. For earlier versions, assume the compiler is MSVC 6.
161 """
162
163 prefix = "MSC v."
164 i = sys.version.find(prefix)
165 if i == -1:
166 return 6
167 i = i + len(prefix)
168 s, rest = sys.version[i:].split(" ", 1)
169 majorVersion = int(s[:-2]) - 6
170 minorVersion = int(s[2:3]) / 10.0
171 # I don't think paths are affected by minor version in version 6
172 if majorVersion == 6:
173 minorVersion = 0
174 if majorVersion >= 6:
175 return majorVersion + minorVersion
176 # else we don't know what version of the compiler this is
177 return None
178
179
180def get_build_architecture():
181 """Return the processor architecture.
182
183 Possible results are "Intel", "Itanium", or "AMD64".
184 """
185
186 prefix = " bit ("
187 i = sys.version.find(prefix)
188 if i == -1:
189 return "Intel"
190 j = sys.version.find(")", i)
191 return sys.version[i+len(prefix):j]
192
193
194def normalize_and_reduce_paths(paths):
195 """Return a list of normalized paths with duplicates removed.
196
197 The current order of paths is maintained.
198 """
199 # Paths are normalized so things like: /a and /a/ aren't both preserved.
200 reduced_paths = []
201 for p in paths:
202 np = os.path.normpath(p)
203 # XXX(nnorwitz): O(n**2), if reduced_paths gets long perhaps use a set.
204 if np not in reduced_paths:
205 reduced_paths.append(np)
206 return reduced_paths
207
208
209class MSVCCompiler(CCompiler):
210 """Concrete class that implements an interface to Microsoft Visual C++,
211 as defined by the CCompiler abstract class."""
212
213 name = 'msvc'
214 description = "Microsoft Visual C++"
215
216 # Just set this so CCompiler's constructor doesn't barf. We currently
217 # don't use the 'set_executables()' bureaucracy provided by CCompiler,
218 # as it really isn't necessary for this sort of single-compiler class.
219 # Would be nice to have a consistent interface with UnixCCompiler,
220 # though, so it's worth thinking about.
221 executables = {}
222
223 # Private class data (need to distinguish C from C++ source for compiler)
224 _c_extensions = ['.c']
225 _cpp_extensions = ['.cc', '.cpp', '.cxx']
226 _rc_extensions = ['.rc']
227 _mc_extensions = ['.mc']
228
229 # Needed for the filename generation methods provided by the
230 # base class, CCompiler.
231 src_extensions = (_c_extensions + _cpp_extensions +
232 _rc_extensions + _mc_extensions)
233 res_extension = '.res'
234 obj_extension = '.obj'
235 static_lib_extension = '.lib'
236 shared_lib_extension = '.dll'
237 static_lib_format = shared_lib_format = '%s%s'
238 exe_extension = '.exe'
239
240 def __init__(self, verbose=0, dry_run=False, force=False):
241 CCompiler.__init__(self, verbose, dry_run, force)
242 self.__version = get_build_version()
243 self.__arch = get_build_architecture()
244 if self.__arch == "Intel":
245 # x86
246 if self.__version >= 7:
247 self.__root = r"Software\Microsoft\VisualStudio"
248 self.__macros = MacroExpander(self.__version)
249 else:
250 self.__root = r"Software\Microsoft\Devstudio"
251 self.__product = "Visual Studio version %s" % self.__version
252 else:
253 # Win64. Assume this was built with the platform SDK
254 self.__product = "Microsoft SDK compiler %s" % (self.__version + 6)
255
256 self.initialized = False
257
258 def initialize(self):
259 self.__paths = []
260 if ("DISTUTILS_USE_SDK" in os.environ and "MSSdk" in os.environ and
261 self.find_exe("cl.exe")):
262 # Assume that the SDK set up everything alright; don't try to be
263 # smarter
264 self.cc = "cl.exe"
265 self.linker = "link.exe"
266 self.lib = "lib.exe"
267 self.rc = "rc.exe"
268 self.mc = "mc.exe"
269 else:
270 self.__paths = self.get_msvc_paths("path")
271
272 if len(self.__paths) == 0:
273 raise PackagingPlatformError("Python was built with %s "
274 "and extensions need to be built with the same "
275 "version of the compiler, but it isn't installed." %
276 self.__product)
277
278 self.cc = self.find_exe("cl.exe")
279 self.linker = self.find_exe("link.exe")
280 self.lib = self.find_exe("lib.exe")
281 self.rc = self.find_exe("rc.exe") # resource compiler
282 self.mc = self.find_exe("mc.exe") # message compiler
283 self.set_path_env_var('lib')
284 self.set_path_env_var('include')
285
286 # extend the MSVC path with the current path
287 try:
288 for p in os.environ['path'].split(';'):
289 self.__paths.append(p)
290 except KeyError:
291 pass
292 self.__paths = normalize_and_reduce_paths(self.__paths)
293 os.environ['path'] = ';'.join(self.__paths)
294
295 self.preprocess_options = None
296 if self.__arch == "Intel":
297 self.compile_options = ['/nologo', '/Ox', '/MD', '/W3', '/GX',
298 '/DNDEBUG']
299 self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/GX',
300 '/Z7', '/D_DEBUG']
301 else:
302 # Win64
303 self.compile_options = ['/nologo', '/Ox', '/MD', '/W3', '/GS-',
304 '/DNDEBUG']
305 self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/GS-',
306 '/Z7', '/D_DEBUG']
307
308 self.ldflags_shared = ['/DLL', '/nologo', '/INCREMENTAL:NO']
309 if self.__version >= 7:
310 self.ldflags_shared_debug = [
311 '/DLL', '/nologo', '/INCREMENTAL:no', '/DEBUG'
312 ]
313 else:
314 self.ldflags_shared_debug = [
315 '/DLL', '/nologo', '/INCREMENTAL:no', '/pdb:None', '/DEBUG'
316 ]
317 self.ldflags_static = [ '/nologo']
318
319 self.initialized = True
320
321 # -- Worker methods ------------------------------------------------
322
323 def object_filenames(self, source_filenames, strip_dir=False, output_dir=''):
324 # Copied from ccompiler.py, extended to return .res as 'object'-file
325 # for .rc input file
326 if output_dir is None:
327 output_dir = ''
328 obj_names = []
329 for src_name in source_filenames:
330 base, ext = os.path.splitext(src_name)
331 base = os.path.splitdrive(base)[1] # Chop off the drive
332 base = base[os.path.isabs(base):] # If abs, chop off leading /
333 if ext not in self.src_extensions:
334 # Better to raise an exception instead of silently continuing
335 # and later complain about sources and targets having
336 # different lengths
337 raise CompileError("Don't know how to compile %s" % src_name)
338 if strip_dir:
339 base = os.path.basename(base)
340 if ext in self._rc_extensions:
341 obj_names.append(os.path.join(output_dir,
342 base + self.res_extension))
343 elif ext in self._mc_extensions:
344 obj_names.append(os.path.join(output_dir,
345 base + self.res_extension))
346 else:
347 obj_names.append(os.path.join(output_dir,
348 base + self.obj_extension))
349 return obj_names
350
351 def compile(self, sources,
352 output_dir=None, macros=None, include_dirs=None, debug=False,
353 extra_preargs=None, extra_postargs=None, depends=None):
354
355 if not self.initialized:
356 self.initialize()
357 macros, objects, extra_postargs, pp_opts, build = \
358 self._setup_compile(output_dir, macros, include_dirs, sources,
359 depends, extra_postargs)
360
361 compile_opts = extra_preargs or []
362 compile_opts.append('/c')
363 if debug:
364 compile_opts.extend(self.compile_options_debug)
365 else:
366 compile_opts.extend(self.compile_options)
367
368 for obj in objects:
369 try:
370 src, ext = build[obj]
371 except KeyError:
372 continue
373 if debug:
374 # pass the full pathname to MSVC in debug mode,
375 # this allows the debugger to find the source file
376 # without asking the user to browse for it
377 src = os.path.abspath(src)
378
379 if ext in self._c_extensions:
380 input_opt = "/Tc" + src
381 elif ext in self._cpp_extensions:
382 input_opt = "/Tp" + src
383 elif ext in self._rc_extensions:
384 # compile .RC to .RES file
385 input_opt = src
386 output_opt = "/fo" + obj
387 try:
388 self.spawn([self.rc] + pp_opts +
389 [output_opt] + [input_opt])
390 except PackagingExecError as msg:
391 raise CompileError(msg)
392 continue
393 elif ext in self._mc_extensions:
394
395 # Compile .MC to .RC file to .RES file.
396 # * '-h dir' specifies the directory for the
397 # generated include file
398 # * '-r dir' specifies the target directory of the
399 # generated RC file and the binary message resource
400 # it includes
401 #
402 # For now (since there are no options to change this),
403 # we use the source-directory for the include file and
404 # the build directory for the RC file and message
405 # resources. This works at least for win32all.
406
407 h_dir = os.path.dirname(src)
408 rc_dir = os.path.dirname(obj)
409 try:
410 # first compile .MC to .RC and .H file
411 self.spawn([self.mc] +
412 ['-h', h_dir, '-r', rc_dir] + [src])
413 base, _ = os.path.splitext(os.path.basename(src))
414 rc_file = os.path.join(rc_dir, base + '.rc')
415 # then compile .RC to .RES file
416 self.spawn([self.rc] +
417 ["/fo" + obj] + [rc_file])
418
419 except PackagingExecError as msg:
420 raise CompileError(msg)
421 continue
422 else:
423 # how to handle this file?
424 raise CompileError(
425 "Don't know how to compile %s to %s" %
426 (src, obj))
427
428 output_opt = "/Fo" + obj
429 try:
430 self.spawn([self.cc] + compile_opts + pp_opts +
431 [input_opt, output_opt] +
432 extra_postargs)
433 except PackagingExecError as msg:
434 raise CompileError(msg)
435
436 return objects
437
438 def create_static_lib(self, objects, output_libname, output_dir=None,
439 debug=False, target_lang=None):
440 if not self.initialized:
441 self.initialize()
442 objects, output_dir = self._fix_object_args(objects, output_dir)
443 output_filename = \
444 self.library_filename(output_libname, output_dir=output_dir)
445
446 if self._need_link(objects, output_filename):
447 lib_args = objects + ['/OUT:' + output_filename]
448 if debug:
449 pass # XXX what goes here?
450 try:
451 self.spawn([self.lib] + lib_args)
452 except PackagingExecError as msg:
453 raise LibError(msg)
454
455 else:
456 logger.debug("skipping %s (up-to-date)", output_filename)
457
458 def link(self, target_desc, objects, output_filename, output_dir=None,
459 libraries=None, library_dirs=None, runtime_library_dirs=None,
460 export_symbols=None, debug=False, extra_preargs=None,
461 extra_postargs=None, build_temp=None, target_lang=None):
462
463 if not self.initialized:
464 self.initialize()
465 objects, output_dir = self._fix_object_args(objects, output_dir)
466 libraries, library_dirs, runtime_library_dirs = \
467 self._fix_lib_args(libraries, library_dirs, runtime_library_dirs)
468
469 if runtime_library_dirs:
470 self.warn("don't know what to do with 'runtime_library_dirs': %s"
471 % (runtime_library_dirs,))
472
473 lib_opts = gen_lib_options(self, library_dirs, runtime_library_dirs,
474 libraries)
475 if output_dir is not None:
476 output_filename = os.path.join(output_dir, output_filename)
477
478 if self._need_link(objects, output_filename):
479
480 if target_desc == CCompiler.EXECUTABLE:
481 if debug:
482 ldflags = self.ldflags_shared_debug[1:]
483 else:
484 ldflags = self.ldflags_shared[1:]
485 else:
486 if debug:
487 ldflags = self.ldflags_shared_debug
488 else:
489 ldflags = self.ldflags_shared
490
491 export_opts = []
492 for sym in (export_symbols or []):
493 export_opts.append("/EXPORT:" + sym)
494
495 ld_args = (ldflags + lib_opts + export_opts +
496 objects + ['/OUT:' + output_filename])
497
498 # The MSVC linker generates .lib and .exp files, which cannot be
499 # suppressed by any linker switches. The .lib files may even be
500 # needed! Make sure they are generated in the temporary build
501 # directory. Since they have different names for debug and release
502 # builds, they can go into the same directory.
503 if export_symbols is not None:
504 dll_name, dll_ext = os.path.splitext(
505 os.path.basename(output_filename))
506 implib_file = os.path.join(
507 os.path.dirname(objects[0]),
508 self.library_filename(dll_name))
509 ld_args.append('/IMPLIB:' + implib_file)
510
511 if extra_preargs:
512 ld_args[:0] = extra_preargs
513 if extra_postargs:
514 ld_args.extend(extra_postargs)
515
516 self.mkpath(os.path.dirname(output_filename))
517 try:
518 self.spawn([self.linker] + ld_args)
519 except PackagingExecError as msg:
520 raise LinkError(msg)
521
522 else:
523 logger.debug("skipping %s (up-to-date)", output_filename)
524
525 # -- Miscellaneous methods -----------------------------------------
526 # These are all used by the 'gen_lib_options() function, in
527 # ccompiler.py.
528
529 def library_dir_option(self, dir):
530 return "/LIBPATH:" + dir
531
532 def runtime_library_dir_option(self, dir):
533 raise PackagingPlatformError("don't know how to set runtime library search path for MSVC++")
534
535 def library_option(self, lib):
536 return self.library_filename(lib)
537
538 def find_library_file(self, dirs, lib, debug=False):
539 # Prefer a debugging library if found (and requested), but deal
540 # with it if we don't have one.
541 if debug:
542 try_names = [lib + "_d", lib]
543 else:
544 try_names = [lib]
545 for dir in dirs:
546 for name in try_names:
547 libfile = os.path.join(dir, self.library_filename(name))
548 if os.path.exists(libfile):
549 return libfile
550 else:
551 # Oops, didn't find it in *any* of 'dirs'
552 return None
553
554 # Helper methods for using the MSVC registry settings
555
556 def find_exe(self, exe):
557 """Return path to an MSVC executable program.
558
559 Tries to find the program in several places: first, one of the
560 MSVC program search paths from the registry; next, the directories
561 in the PATH environment variable. If any of those work, return an
562 absolute path that is known to exist. If none of them work, just
563 return the original program name, 'exe'.
564 """
565
566 for p in self.__paths:
567 fn = os.path.join(os.path.abspath(p), exe)
568 if os.path.isfile(fn):
569 return fn
570
571 # didn't find it; try existing path
572 for p in os.environ['Path'].split(';'):
573 fn = os.path.join(os.path.abspath(p), exe)
574 if os.path.isfile(fn):
575 return fn
576
577 return exe
578
579 def get_msvc_paths(self, path, platform='x86'):
580 """Get a list of devstudio directories (include, lib or path).
581
582 Return a list of strings. The list will be empty if unable to
583 access the registry or appropriate registry keys not found.
584 """
585
586 if not _can_read_reg:
587 return []
588
589 path = path + " dirs"
590 if self.__version >= 7:
591 key = (r"%s\%0.1f\VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories"
592 % (self.__root, self.__version))
593 else:
594 key = (r"%s\6.0\Build System\Components\Platforms"
595 r"\Win32 (%s)\Directories" % (self.__root, platform))
596
597 for base in HKEYS:
598 d = read_values(base, key)
599 if d:
600 if self.__version >= 7:
601 return self.__macros.sub(d[path]).split(";")
602 else:
603 return d[path].split(";")
604 # MSVC 6 seems to create the registry entries we need only when
605 # the GUI is run.
606 if self.__version == 6:
607 for base in HKEYS:
608 if read_values(base, r"%s\6.0" % self.__root) is not None:
609 self.warn("It seems you have Visual Studio 6 installed, "
610 "but the expected registry settings are not present.\n"
611 "You must at least run the Visual Studio GUI once "
612 "so that these entries are created.")
613 break
614 return []
615
616 def set_path_env_var(self, name):
617 """Set environment variable 'name' to an MSVC path type value.
618
619 This is equivalent to a SET command prior to execution of spawned
620 commands.
621 """
622
623 if name == "lib":
624 p = self.get_msvc_paths("library")
625 else:
626 p = self.get_msvc_paths(name)
627 if p:
628 os.environ[name] = ';'.join(p)
629
630
631if get_build_version() >= 8.0:
632 logger.debug("importing new compiler from distutils.msvc9compiler")
633 OldMSVCCompiler = MSVCCompiler
634 from packaging.compiler.msvc9compiler import MSVCCompiler
635 # get_build_architecture not really relevant now we support cross-compile
636 from packaging.compiler.msvc9compiler import MacroExpander